Logo ROOT   6.30.04
Reference Guide
 All Namespaces Files Pages
TSQLFile.cxx
Go to the documentation of this file.
1 // @(#)root/sql:$Id: 6f6608219c30ddefdf8e25d7cf170d5e69704cd3 $
2 // Author: Sergey Linev 20/11/2005
3 
4 /*************************************************************************
5  * Copyright (C) 1995-2005, Rene Brun and Fons Rademakers. *
6  * All rights reserved. *
7  * *
8  * For the licensing terms see $ROOTSYS/LICENSE. *
9  * For the list of contributors see $ROOTSYS/README/CREDITS. *
10  *************************************************************************/
11 
12 /**
13 \class TSQLFile
14 \ingroup IO
15 
16 Access an SQL db via the TFile interface.
17 
18 The main motivation for the TSQLFile development is to have
19 "transparent" access to SQL data base via standard TFile interface.
20 The main approach that each class (but not each object) has one or two tables
21 with names like $(CLASSNAME)_ver$(VERSION) and $(CLASSNAME)_raw$(VERSION)
22 For example: TAxis_ver8 or TList_raw5
23 Second kind of tables appears, when some of class members can not be converted to
24 normalized form or when class has custom streamer.
25 For instance, for TH1 class two tables are required: TH1_ver4 and TH1_raw4
26 Most of members are stored in TH1_ver4 table column-wise, and only member:
27  Double_t* fBuffer; //[fBufferSize]
28 can not be represented as column while size of array is not fixed.
29 Therefore, fBuffer will be written as list of values in TH1_raw4 table.
30 All objects, stored in the DB, will be registered in table "ObjectsTable".
31 In this there are following columns:
32 | Name | Description |
33 |------|-------------|
34 | "key:id" | key identifier to which belong object |
35 | "obj:id" | object identifier |
36 | "Class" | object class name |
37 | "Version" | object class version |
38 
39  Data in each "ObjectsTable" row uniquely identify, in which table
40  and which column object is stored.
41 
42 In normal situation all class data should be sorted column-wise.
43 Up to now following member are supported:
44  -# Basic data types. Here is everything clear. Column SQL type will be as much as possible
45  close to the original type of value.
46  -# Fixed array of basic data types. In this case n columns like fArr[0],
47  fArr[1] and so on will be created.
48  If there is multidimensional array, names will be fArr2[1][2][1] and so on
49  -# Parent class. In this case version of parent class is stored and
50  data of parent class will be stored with the same obj:id in correspondent table.
51  There is a special case, when parent store nothing (this is for instance TQObject).
52  In that case just -1 is written to avoid any extra checks if table exist or not.
53  -# Object as data member. In that case object is saved in normal way to data base and column
54  will contain id of this object.
55  -# Pointer on object. Same as before. In case if object was already stored, just its id
56  will be placed in the column. For NULL pointer 0 is used.
57  -# TString. Now column with limited width like VARCAHR(255) in MySQL is used.
58  Later this will be improved to support maximum possible strings
59  -# Anything else. Data will be converted to raw format and saved in _streamer_ table.
60  Each row supplied with obj:id and row:id, where row:id indicates
61  data, corresponding to this particular data member, and column
62  will contain this raw:id
63 
64 All conversion to SQL statements are done with help of TSQLStructure class.
65 This is special hierarchical structure wich internally is very similar
66 to XML structures. TBufferSQL2 creates these structures, when object
67 data is streamed by ROOT and only afterwards all SQL statements will be produced
68 and applied all together.
69 When data is reading, TBufferSQL2 will produce requests to database
70 during unstreaming of object data.
71 Optionally (default this options on) name of column includes
72 suffix which indicates type of column. For instance:
73 | Name | Description |
74 |------|-------------|
75 | *:parent | parent class, column contain class version |
76 | *:object | other object, column contain object id |
77 | *:rawdata | raw data, column contains id of raw data from _streamer_ table |
78 | *:Int_t | column with integer value |
79 
80 Use TSQLFile::SetUseSuffixes(kFALSE) to disable suffixes usage.
81 This and several other options can be changed only when
82 TSQLFile created with options "CREATE" or "RECREATE" and only before
83 first write operation. These options are:
84 | Name | Description |
85 |------|-------------|
86 | SetUseSuffixes() | suffix usage in column names (default - on) |
87 | SetArrayLimit() | defines maximum array size, which can has column for each element (default 21) |
88 | SetTablesType() | table type name in MySQL database (default "InnoDB") |
89 | SetUseIndexes() | usage of indexes in database (default kIndexesBasic) |
90 
91 Normally these functions should be called immediately after TSQLFile constructor.
92 When objects data written to database, by default START TRANSACTION/COMMIT
93 SQL commands are used before and after data storage. If TSQLFile detects
94 any problems, ROLLBACK command will be used to restore
95 previous state of data base. If transactions not supported by SQL server,
96 they can be disabled by SetUseTransactions(kTransactionsOff). Or user
97 can take responsibility to use transactions function himself.
98 By default only indexes for basic tables are created.
99 In most cases usage of indexes increase performance to data reading,
100 but it also can increase time of writing data to database.
101 There are several modes of index usage available in SetUseIndexes() method
102 There is MakeSelectQuery(TClass*) method, which
103 produces SELECT statement to get objects data of specified class.
104 Difference from simple statement like:
105  mysql> SELECT * FROM TH1I_ver1
106 that not only data for that class, but also data from parent classes
107 will be extracted from other tables and combined in single result table.
108 Such select query can be useful for external access to objects data.
109 
110 Up to now MySQL 4.1 and Oracle 9i were tested.
111 Some extra work is required for other SQL databases.
112 Hopefully, this should be straightforward.
113 
114 Known problems and open questions.
115  -# TTree is not supported by TSQLFile. There is independent development
116  of TTreeSQL class, which allows to store trees directly in SQL database
117  -# TClonesArray is store objects in raw format,
118  which can not be accessed outside ROOT.
119  This will be changed later.
120  -# TDirectory cannot work. Hopefully, will (changes in ROOT basic I/O is required)
121  -# Streamer infos are not written to file, therefore schema evolution
122  is not yet supported. All eforts are done to enable this feature in
123  the near future
124 
125 ### Example how TSQLFile can be used
126 
127 #### A session saving data to a SQL data base
128 ~~~{.cpp}
129 auto dbname = "mysql://host.domain:3306/dbname";
130 auto username = "username";
131 auto userpass = "userpass";
132 
133 // Clean data base and create primary tables
134 auto f = new TSQLFile(dbname, "recreate", username, userpass);
135 // Write with standard I/O functions
136 arr->Write("arr", TObject::kSingleKey);
137 h1->Write("histo");
138 // Close connection to DB
139 delete f;
140 ~~~
141 
142 #### A session read data from SQL data base
143 ~~~{.cpp}
144 // Open database again in read-only mode
145 auto f = new TSQLFile(dbname, "open", username, userpass);
146 // Show list of keys
147 f->ls();
148 // Read stored object, again standard ROOT I/O
149 auto h1 = (TH1*) f->Get("histo");
150 if (h1!=0) { h1->SetDirectory(0); h1->Draw(); }
151 auto obj = f->Get("arr");
152 if (obj!=0) obj->Print("*");
153 // close connection to DB
154 delete f;
155 ~~~
156 
157 The "SQL I/O" package is currently under development.
158 Any bug reports and suggestions are welcome.
159 Author: S.Linev, GSI Darmstadt, S.Linev@gsi.de
160 */
161 
162 #include "TSQLFile.h"
163 
164 #include "TROOT.h"
165 #include "TSystem.h"
166 #include "TList.h"
167 #include "TObjArray.h"
168 #include "TObjString.h"
169 #include "TList.h"
170 #include "TArrayC.h"
171 #include "TVirtualStreamerInfo.h"
172 #include "TStreamerElement.h"
173 #include "TProcessID.h"
174 #include "TError.h"
175 #include "TClass.h"
176 #include "TVirtualMutex.h"
177 
178 #include "TSQLServer.h"
179 #include "TSQLTableInfo.h"
180 #include "TSQLColumnInfo.h"
181 #include "TSQLStatement.h"
182 #include "TSQLResult.h"
183 #include "TSQLRow.h"
184 #include "TBufferSQL2.h"
185 #include "TSQLStructure.h"
186 #include "TKeySQL.h"
187 #include "TSQLClassInfo.h"
188 #include "TSQLObjectData.h"
189 
190 #include "Riostream.h"
191 
192 ClassImp(TSQLFile);
193 
194 const char *mysql_BasicTypes[21] = {"VARCHAR(255)", // kBase = 0, used for text
195  "TINYINT UNSIGNED", // kChar = 1,
196  "SMALLINT", // kShort = 2,
197  "INT", // kInt = 3,
198  "BIGINT", // kLong = 4,
199  "FLOAT", // kFloat = 5,
200  "INT", // kCounter = 6,
201  "VARCHAR(255)", // kCharStar = 7,
202  "DOUBLE", // kDouble = 8,
203  "DOUBLE", // kDouble32= 9,
204  "", // nothing
205  "TINYINT UNSIGNED", // kUChar = 11,
206  "SMALLINT UNSIGNED", // kUShort = 12,
207  "INT UNSIGNED", // kUInt = 13,
208  "BIGINT UNSIGNED", // kULong = 14,
209  "INT UNSIGNED", // kBits = 15,
210  "BIGINT", // kLong64 = 16,
211  "BIGINT UNSIGNED", // kULong64 = 17,
212  "BOOL", // kBool = 18,
213  "DOUBLE", // kFloat16 = 19,
214  ""};
215 
216 const char *mysql_OtherTypes[13] = {
217  "VARCHAR(255)", // smallest text
218  "255", // maximum length of small text
219  "TEXT", // biggest size text
220  "DATETIME", // date & time
221  "`", // quote for identifier like table name or column name
222  "dir:id", // dir id column
223  "key:id", // key id column
224  "obj:id", // object id column
225  "raw:id", // raw data id column
226  "str:id", // string id column
227  ":", // name separator between name and type like TObject:Parent
228  "\"", // quote for string values in MySQL
229  "InnoDB" // default tables types, used only for MySQL tables
230 };
231 
232 const char *oracle_BasicTypes[21] = {"VARCHAR(255)", // kBase = 0, used for text
233  "INT", // kChar = 1,
234  "INT", // kShort = 2,
235  "INT", // kInt = 3,
236  "INT", // kLong = 4,
237  "FLOAT", // kFloat = 5,
238  "INT", // kCounter = 6,
239  "VARCHAR(255)", // kCharStar = 7,
240  "DOUBLE PRECISION", // kDouble = 8,
241  "DOUBLE PRECISION", // kDouble32= 9,
242  "", // nothing
243  "INT", // kUChar = 11,
244  "INT", // kUShort = 12,
245  "INT", // kUInt = 13,
246  "INT", // kULong = 14,
247  "INT", // kBits = 15,
248  "INT", // kLong64 = 16,
249  "INT", // kULong64 = 17,
250  "INT", // kBool = 18,
251  "FLOAT", // kFloat16 = 19,
252  ""};
253 
254 const char *oracle_OtherTypes[13] = {
255  "VARCHAR(1000)", // smallest text
256  "1000", // maximum size of smallest text
257  "VARCHAR(4000)", // biggest size text, CLOB is not yet supported by TOracleRow
258  "VARCHAR(50)", // date & time
259  "\"", // quote for identifier like table name or column name
260  "dir:id", // dir id column
261  "key:id", // key id column
262  "obj:id", // object id column
263  "raw:id", // raw data id column
264  "str:id", // string id column
265  ":", // name separator between name and type like TObject:parent
266  "'", // quote for string values in Oracle
267  "" // default tables types, used only for MySQL tables
268 };
269 
270 ////////////////////////////////////////////////////////////////////////////////
271 /// default TSQLFile constructor
272 
273 TSQLFile::TSQLFile()
274  : TFile(), fSQL(0), fSQLClassInfos(0), fUseSuffixes(kTRUE), fSQLIOversion(1), fArrayLimit(21),
275  fCanChangeConfig(kFALSE), fTablesType(), fUseTransactions(0), fUseIndexes(0), fModifyCounter(0), fQuerisCounter(0),
276  fBasicTypes(0), fOtherTypes(0), fUserName(), fLogFile(0), fIdsTableExists(kFALSE), fStmtCounter(0)
277 {
278  SetBit(kBinaryFile, kFALSE);
279 }
280 
281 ////////////////////////////////////////////////////////////////////////////////
282 /// Connects to SQL server with provided arguments.
283 ///
284 /// If the constructor fails in any way IsZombie() will
285 /// return true. Use IsOpen() to check if the file is (still) open.
286 /// | Option | Description |
287 /// |--------|-------------|
288 /// | NEW or CREATE | Create a ROOT tables in database if the tables already exists connection is not opened.|
289 /// | RECREATE | Create completely new tables. Any existing table will be deleted.|
290 /// | UPDATE | Open an existing database for writing. If data base open by other TSQLFile instance for writing,
291 /// write access will be rejected.|
292 /// | BREAKLOCK | Special case when lock was not correctly released by TSQLFile instance. This may happen if
293 /// program crashed when TSQLFile was open with write access mode.|
294 /// | READ / OPEN | Open an existing data base for reading.|
295 ///
296 /// For more details see comments for TFile::TFile() constructor.
297 /// For a moment TSQLFile does not support TTree objects and subdirectories.
298 
299 TSQLFile::TSQLFile(const char *dbname, Option_t *option, const char *user, const char *pass)
300  : TFile(), fSQL(0), fSQLClassInfos(0), fUseSuffixes(kTRUE), fSQLIOversion(1), fArrayLimit(21),
301  fCanChangeConfig(kFALSE), fTablesType(), fUseTransactions(0), fUseIndexes(0), fModifyCounter(0), fQuerisCounter(0),
302  fBasicTypes(mysql_BasicTypes), fOtherTypes(mysql_OtherTypes), fUserName(user), fLogFile(0),
303  fIdsTableExists(kFALSE), fStmtCounter(0)
304 {
305  if (!gROOT)
306  ::Fatal("TFile::TFile", "ROOT system not initialized");
307 
308  gDirectory = 0;
309  SetName(dbname);
310  SetTitle("TFile interface to SQL DB");
311  TDirectoryFile::Build();
312  fFile = this;
313 
314  if (dbname && strstr(dbname, "oracle://") != 0) {
315  fBasicTypes = oracle_BasicTypes;
316  fOtherTypes = oracle_OtherTypes;
317  }
318 
319  fArrayLimit = 21;
320  fTablesType = SQLDefaultTableType();
321  fUseIndexes = 1;
322  fUseTransactions = kTransactionsAuto;
323 
324  fD = -1;
325  fFile = this;
326  fFree = 0;
327  fVersion = gROOT->GetVersionInt(); // ROOT version in integer format
328  fUnits = 4;
329  fOption = option;
330  SetCompressionLevel(ROOT::RCompressionSetting::EDefaults::kUseCompiledDefault % 100);
331  fWritten = 0;
332  fSumBuffer = 0;
333  fSum2Buffer = 0;
334  fBytesRead = 0;
335  fBytesWrite = 0;
336  fClassIndex = 0;
337  fSeekInfo = 0;
338  fNbytesInfo = 0;
339  fProcessIDs = nullptr;
340  fNProcessIDs = 0;
341  fSeekDir = sqlio::Ids_RootDir;
342  SetBit(kBinaryFile, kFALSE);
343 
344  fOption = option;
345  fOption.ToUpper();
346 
347  if (fOption == "NEW")
348  fOption = "CREATE";
349 
350  Bool_t breaklock = kFALSE;
351 
352  if (fOption == "BREAKLOCK") {
353  breaklock = kTRUE;
354  fOption = "UPDATE";
355  }
356 
357  Bool_t create = (fOption == "CREATE") ? kTRUE : kFALSE;
358  Bool_t recreate = (fOption == "RECREATE") ? kTRUE : kFALSE;
359  Bool_t update = (fOption == "UPDATE") ? kTRUE : kFALSE;
360  Bool_t read = (fOption == "READ") ? kTRUE : kFALSE;
361 
362  if (!create && !recreate && !update && !read) {
363  read = kTRUE;
364  fOption = "READ";
365  }
366 
367  if (!dbname || !dbname[0]) {
368  Error("TSQLFile", "Database not specified");
369  goto zombie;
370  }
371 
372  gROOT->cd();
373 
374  fSQL = TSQLServer::Connect(dbname, user, pass);
375 
376  if (fSQL == 0) {
377  Error("TSQLFile", "Cannot connect to DB %s", dbname);
378  goto zombie;
379  }
380 
381  if (recreate) {
382  if (IsTablesExists())
383  if (!IsWriteAccess()) {
384  Error("TSQLFile", "no write permission, DB %s locked", dbname);
385  goto zombie;
386  }
387  SQLDeleteAllTables();
388  recreate = kFALSE;
389  create = kTRUE;
390  fOption = "CREATE";
391  }
392 
393  if (create && IsTablesExists()) {
394  Error("TSQLFile", "DB tables already exists");
395  goto zombie;
396  }
397 
398  if (update) {
399  if (!IsTablesExists()) {
400  update = kFALSE;
401  create = kTRUE;
402  }
403 
404  if (update && !breaklock && !IsWriteAccess()) {
405  Error("TSQLFile", "no write permission, DB %s locked", dbname);
406  goto zombie;
407  }
408  }
409 
410  if (read) {
411  if (!IsTablesExists()) {
412  Error("TSQLFile", "DB %s tables not exist", dbname);
413  goto zombie;
414  }
415  if (!IsReadAccess()) {
416  Error("TSQLFile", "no read permission for DB %s tables", dbname);
417  goto zombie;
418  }
419  }
420 
421  fRealName = dbname;
422 
423  if (create || update) {
424  SetWritable(kTRUE);
425  if (update)
426  SetLocking(kLockBusy);
427  } else
428  SetWritable(kFALSE);
429 
430  // user can change configurations only when create (recreate) options
431  // was specified. When first object will be saved, configurations will
432  // be frozen.
433  fCanChangeConfig = create;
434 
435  InitSqlDatabase(create);
436 
437  return;
438 
439 zombie:
440 
441  delete fSQL;
442  fSQL = 0;
443  MakeZombie();
444  gDirectory = gROOT;
445 }
446 
447 ////////////////////////////////////////////////////////////////////////////////
448 /// start logging of all SQL statements in specified file
449 
450 void TSQLFile::StartLogFile(const char *fname)
451 {
452  StopLogFile();
453  fLogFile = new std::ofstream(fname);
454 }
455 
456 ////////////////////////////////////////////////////////////////////////////////
457 /// close logging file
458 
459 void TSQLFile::StopLogFile()
460 {
461  if (fLogFile != 0) {
462  delete fLogFile;
463  fLogFile = 0;
464  }
465 }
466 
467 ////////////////////////////////////////////////////////////////////////////////
468 /// checks, if MySQL database
469 
470 Bool_t TSQLFile::IsMySQL() const
471 {
472  if (fSQL == 0)
473  return kFALSE;
474  return strcmp(fSQL->ClassName(), "TMySQLServer") == 0;
475 }
476 
477 ////////////////////////////////////////////////////////////////////////////////
478 /// checks, if Oracle database
479 
480 Bool_t TSQLFile::IsOracle() const
481 {
482  if (fSQL == 0)
483  return kFALSE;
484  return strcmp(fSQL->ClassName(), "TOracleServer") == 0;
485 }
486 
487 ////////////////////////////////////////////////////////////////////////////////
488 /// checks, if ODBC driver used for database connection
489 
490 Bool_t TSQLFile::IsODBC() const
491 {
492  if (fSQL == 0)
493  return kFALSE;
494  return strcmp(fSQL->ClassName(), "TODBCServer") == 0;
495 }
496 
497 ////////////////////////////////////////////////////////////////////////////////
498 /// enable/disable uasge of suffixes in columns names
499 /// can be changed before first object is saved into file
500 
501 void TSQLFile::SetUseSuffixes(Bool_t on)
502 {
503  if (!fCanChangeConfig)
504  Error("SetUseSuffixes", "Configurations already cannot be changed");
505  else
506  fUseSuffixes = on;
507 }
508 
509 ////////////////////////////////////////////////////////////////////////////////
510 /// Defines maximum number of columns for array representation
511 /// If array size bigger than limit, array data will be converted to raw format
512 /// This is usefull to prevent tables with very big number of columns
513 /// If limit==0, all arrays will be stored in raw format
514 /// If limit<0, all array values will be stored in column form
515 /// Default value is 21
516 
517 void TSQLFile::SetArrayLimit(Int_t limit)
518 {
519  if (!fCanChangeConfig)
520  Error("SetArrayLimit", "Configurations already cannot be changed");
521  else
522  fArrayLimit = limit;
523 }
524 
525 ////////////////////////////////////////////////////////////////////////////////
526 /// Defines tables type, which is used in CREATE TABLE statements
527 /// Now is only used for MySQL database, where following types are supported:
528 /// "BDB", "HEAP", "ISAM", "InnoDB", "MERGE", "MRG_MYISAM", "MYISAM"
529 /// Default for TSQLFile is "InnoDB". For more detailes see MySQL docs.
530 
531 void TSQLFile::SetTablesType(const char *tables_type)
532 {
533  if (!fCanChangeConfig)
534  Error("SetTablesType", "Configurations already cannot be changed");
535  else
536  fTablesType = tables_type;
537 }
538 
539 ////////////////////////////////////////////////////////////////////////////////
540 /// Defines usage of transactions statements for writing objects data to database.
541 /// | Index | Description |
542 /// |-------|-------------|
543 /// | kTransactionsOff=0 - no transaction operation are allowed |
544 /// | kTransactionsAuto=1 - automatic mode. Each write operation, produced by TSQLFile, will be supplied by START
545 /// TRANSACTION and COMMIT calls. If any error happen, ROLLBACK will returns database to previous state |
546 /// | kTransactionsUser=2 - transactions are delegated to user. Methods StartTransaction(), Commit() and Rollback()
547 /// should be called by user. |
548 ///
549 /// Default UseTransactions option is kTransactionsAuto
550 
551 void TSQLFile::SetUseTransactions(Int_t mode)
552 {
553  fUseTransactions = mode;
554 }
555 
556 ////////////////////////////////////////////////////////////////////////////////
557 /// Start user transaction.
558 ///
559 /// This can be usesful, when big number of objects should be stored in
560 /// data base and commitment required only if all operations were successful.
561 /// In that case in the end of all operations method Commit() should be
562 /// called. If operation on user-level is looks like not successful,
563 /// method Rollback() will return database data and TSQLFile instance to
564 /// previous state.
565 /// In MySQL not all tables types support transaction mode of operation.
566 /// See SetTablesType() method for details .
567 
568 Bool_t TSQLFile::StartTransaction()
569 {
570  if (GetUseTransactions() != kTransactionsUser) {
571  Error("SQLStartTransaction", "Only allowed when SetUseTransactions(kUserTransactions) was configured");
572  return kFALSE;
573  }
574 
575  return SQLStartTransaction();
576 }
577 
578 ////////////////////////////////////////////////////////////////////////////////
579 /// Commit transaction, started by StartTransaction() call.
580 /// Only after that call data will be written and visible on database side.
581 
582 Bool_t TSQLFile::Commit()
583 {
584  if (GetUseTransactions() != kTransactionsUser) {
585  Error("SQLCommit", "Only allowed when SetUseTransactions(kUserTransactions) was configured");
586  return kFALSE;
587  }
588 
589  return SQLCommit();
590 }
591 
592 ////////////////////////////////////////////////////////////////////////////////
593 /// Rollback all operations, done after StartTransaction() call.
594 /// Database should return to initial state.
595 
596 Bool_t TSQLFile::Rollback()
597 {
598  if (GetUseTransactions() != kTransactionsUser) {
599  Error("SQLRollback", "Only allowed when SetUseTransactions(kUserTransactions) was configured");
600  return kFALSE;
601  }
602 
603  return SQLRollback();
604 }
605 
606 ////////////////////////////////////////////////////////////////////////////////
607 /// Specify usage of indexes for data tables
608 /// | Index | Description |
609 /// |-------|-------------|
610 /// | kIndexesNone = 0 | no indexes are used|
611 /// | kIndexesBasic = 1 | indexes used only for keys list and objects list tables (default)|
612 /// | kIndexesClass = 2 | index also created for every normal class table|
613 /// | kIndexesAll = 3 | index created for every table, including _streamer_ tables|
614 ///
615 /// Indexes in general should increase speed of access to objects data,
616 /// but they required more operations and more disk space on server side
617 
618 void TSQLFile::SetUseIndexes(Int_t use_type)
619 {
620  if (!fCanChangeConfig)
621  Error("SetUseIndexes", "Configurations already cannot be changed");
622  else
623  fUseIndexes = use_type;
624 }
625 
626 ////////////////////////////////////////////////////////////////////////////////
627 /// Return name of data base on the host
628 /// For Oracle always return 0
629 
630 const char *TSQLFile::GetDataBaseName() const
631 {
632  if (IsOracle())
633  return 0;
634  const char *name = strrchr(GetName(), '/');
635  if (name == 0)
636  return 0;
637  return name + 1;
638 }
639 
640 ////////////////////////////////////////////////////////////////////////////////
641 /// Close a SQL file
642 /// For more comments see TFile::Close() function
643 
644 void TSQLFile::Close(Option_t *option)
645 {
646  if (!IsOpen())
647  return;
648 
649  TString opt = option;
650  if (opt.Length() > 0)
651  opt.ToLower();
652 
653  if (IsWritable()) {
654  SaveToDatabase();
655  SetLocking(kLockFree);
656  }
657 
658  fWritable = kFALSE;
659 
660  if (fClassIndex) {
661  delete fClassIndex;
662  fClassIndex = nullptr;
663  }
664 
665  {
666  TDirectory::TContext ctxt(this);
667  // Delete all supported directories structures from memory
668  TDirectoryFile::Close();
669  }
670 
671  // delete the TProcessIDs
672  TList pidDeleted;
673  TIter next(fProcessIDs);
674  TProcessID *pid;
675  while ((pid = (TProcessID *)next())) {
676  if (!pid->DecrementCount()) {
677  if (pid != TProcessID::GetSessionProcessID())
678  pidDeleted.Add(pid);
679  } else if (opt.Contains("r")) {
680  pid->Clear();
681  }
682  }
683  pidDeleted.Delete();
684 
685  R__LOCKGUARD(gROOTMutex);
686  gROOT->GetListOfFiles()->Remove(this);
687 }
688 
689 ////////////////////////////////////////////////////////////////////////////////
690 /// destructor of TSQLFile object
691 
692 TSQLFile::~TSQLFile()
693 {
694  Close();
695 
696  if (fSQLClassInfos) {
697  fSQLClassInfos->Delete();
698  delete fSQLClassInfos;
699  fSQLClassInfos = nullptr;
700  }
701 
702  StopLogFile();
703 
704  if (fSQL) {
705  delete fSQL;
706  fSQL = nullptr;
707  }
708 }
709 
710 ////////////////////////////////////////////////////////////////////////////////
711 /// return kTRUE if file is opened and can be accessed
712 
713 Bool_t TSQLFile::IsOpen() const
714 {
715  return fSQL != nullptr;
716 }
717 
718 ////////////////////////////////////////////////////////////////////////////////
719 /// Reopen a file with a different access mode, like from READ to
720 /// See TFile::Open() for details
721 
722 Int_t TSQLFile::ReOpen(Option_t *mode)
723 {
724  cd();
725 
726  TString opt = mode;
727  opt.ToUpper();
728 
729  if (opt != "READ" && opt != "UPDATE") {
730  Error("ReOpen", "mode must be either READ or UPDATE, not %s", opt.Data());
731  return 1;
732  }
733 
734  if (opt == fOption || (opt == "UPDATE" && fOption == "CREATE"))
735  return 1;
736 
737  if (opt == "READ") {
738  // switch to READ mode
739 
740  if (IsOpen() && IsWritable()) {
741  SaveToDatabase();
742  SetLocking(kLockFree);
743  }
744  fOption = opt;
745 
746  SetWritable(kFALSE);
747 
748  } else {
749  // switch to UPDATE mode
750 
751  if (!IsWriteAccess()) {
752  Error("ReOpen", "Tables are locked, no write access");
753  return 1;
754  }
755 
756  fOption = opt;
757 
758  SetWritable(kTRUE);
759 
760  SetLocking(kLockBusy);
761  }
762 
763  return 0;
764 }
765 
766 ////////////////////////////////////////////////////////////////////////////////
767 /// create SQL key, which will store object in data base
768 
769 TKey *TSQLFile::CreateKey(TDirectory *mother, const TObject *obj, const char *name, Int_t)
770 {
771  return new TKeySQL(mother, obj, name);
772 }
773 
774 ////////////////////////////////////////////////////////////////////////////////
775 /// create SQL key, which will store object in data base
776 
777 TKey *TSQLFile::CreateKey(TDirectory *mother, const void *obj, const TClass *cl, const char *name, Int_t)
778 {
779  return new TKeySQL(mother, obj, cl, name);
780 }
781 
782 ////////////////////////////////////////////////////////////////////////////////
783 /// Write file info like configurations, title, UUID and other
784 
785 void TSQLFile::WriteHeader()
786 {
787  WriteSpecialObject(sqlio::Ids_TSQLFile, this, GetName(), GetTitle());
788 }
789 
790 ////////////////////////////////////////////////////////////////////////////////
791 /// Store all TVirtualStreamerInfo, used in file, in sql database
792 
793 void TSQLFile::WriteStreamerInfo()
794 {
795  // return;
796 
797  // do not write anything when no basic tables was created
798  if (!IsTablesExists())
799  return;
800 
801  if (gDebug > 1)
802  Info("WriteStreamerInfo", "Saving streamer infos to database");
803 
804  TList list;
805 
806  TIter iter(gROOT->GetListOfStreamerInfo());
807 
808  TVirtualStreamerInfo *info = 0;
809 
810  while ((info = (TVirtualStreamerInfo *)iter()) != 0) {
811  Int_t uid = info->GetNumber();
812  if (fClassIndex->fArray[uid]) {
813  if (gDebug > 1)
814  Info("WriteStreamerInfo", "Add %s", info->GetName());
815  list.Add(info);
816  }
817  }
818  if (list.GetSize() == 0)
819  return;
820  fClassIndex->fArray[0] = 2; // to prevent adding classes in TVirtualStreamerInfo::TagFile
821 
822  WriteSpecialObject(sqlio::Ids_StreamerInfos, &list, "StreamerInfo", "StreamerInfos of this file");
823 
824  fClassIndex->fArray[0] = 0; // to prevent adding classes in TVirtualStreamerInfo::TagFile
825 }
826 
827 ////////////////////////////////////////////////////////////////////////////////
828 /// write special kind of object like streamer infos or file itself
829 /// keys for that objects should exist in tables but not indicated in list of keys,
830 /// therefore users can not get them with TDirectoryFile::Get() method
831 
832 Bool_t TSQLFile::WriteSpecialObject(Long64_t keyid, TObject *obj, const char *name, const char *title)
833 {
834  DeleteKeyFromDB(keyid);
835  if (!obj)
836  return kTRUE;
837 
838  Long64_t objid = StoreObjectInTables(keyid, obj, obj->IsA());
839 
840  if (objid > 0) {
841  TDatime now;
842 
843  TKeySQL *key = new TKeySQL(this, keyid, objid, name, title, now.AsSQLString(), 1, obj->ClassName());
844  WriteKeyData(key);
845  delete key;
846  }
847 
848  return (objid > 0);
849 }
850 
851 ////////////////////////////////////////////////////////////////////////////////
852 /// Read data of special kind of objects
853 
854 TObject *TSQLFile::ReadSpecialObject(Long64_t keyid, TObject *obj)
855 {
856  TKeySQL *key = nullptr;
857 
858  StreamKeysForDirectory(this, kFALSE, keyid, &key);
859  if (!key)
860  return obj;
861 
862  TBufferSQL2 buffer(TBuffer::kRead, this);
863 
864  buffer.InitMap();
865 
866  TClass *cl = nullptr;
867 
868  void *res = buffer.SqlReadAny(key->GetDBKeyId(), key->GetDBObjId(), &cl, obj);
869 
870  if ((cl == TSQLFile::Class()) && (res != nullptr) && (obj == this)) {
871  // name should not be preserved while name of database may be changed
872  SetTitle(key->GetTitle());
873  }
874 
875  delete key;
876 
877  return (TObject *)res;
878 }
879 
880 ////////////////////////////////////////////////////////////////////////////////
881 /// Read back streamer infos from database
882 /// List of streamer infos is always stored with key:id 0,
883 /// which is not shown in normal keys list
884 
885 TFile::InfoListRet TSQLFile::GetStreamerInfoListImpl(bool /* lookupSICache */)
886 {
887  // return new TList;
888 
889  if (gDebug > 1)
890  Info("GetStreamerInfoList", "Start reading of streamer infos");
891 
892  ROOT::Internal::RConcurrentHashColl::HashValue hash;
893 
894  TObject *obj = ReadSpecialObject(sqlio::Ids_StreamerInfos);
895 
896  TList *list = dynamic_cast<TList *>(obj);
897  if (!list) {
898  delete obj;
899  return {nullptr, 1, hash};
900  }
901 
902  return {list, 0, hash};
903 }
904 
905 ////////////////////////////////////////////////////////////////////////////////
906 /// save data which is not yet in Database
907 /// Typically this is streamerinfos structures or
908 
909 void TSQLFile::SaveToDatabase()
910 {
911  if (!fSQL)
912  return;
913 
914  WriteStreamerInfo();
915  WriteHeader();
916 }
917 
918 ////////////////////////////////////////////////////////////////////////////////
919 /// read keys for specified directory (when update == kFALSE)
920 /// or update value for modified keys when update == kTRUE
921 /// Returns number of successfully read keys or -1 if error
922 
923 Int_t TSQLFile::StreamKeysForDirectory(TDirectory *dir, Bool_t doupdate, Long64_t specialkeyid, TKeySQL **specialkey)
924 {
925  if (!dir)
926  return -1;
927 
928  const char *quote = SQLIdentifierQuote();
929 
930  Long64_t dirid = dir->GetSeekDir();
931 
932  TString sqlcmd;
933  sqlcmd.Form("SELECT * FROM %s%s%s WHERE %s%s%s=%lld", quote, sqlio::KeysTable, quote, quote, SQLDirIdColumn(), quote,
934  dirid);
935  if (specialkeyid >= 0) {
936  TString buf;
937  buf.Form(" AND %s%s%s=%lld", quote, SQLKeyIdColumn(), quote, specialkeyid);
938  sqlcmd += buf;
939  }
940 
941  TSQLResult *res = SQLQuery(sqlcmd.Data(), 2);
942 
943  if (!res)
944  return -1;
945 
946  Int_t nkeys = 0;
947 
948  TSQLRow *row = nullptr;
949 
950  while ((row = res->Next()) != nullptr) {
951  nkeys++;
952 
953  Long64_t keyid = sqlio::atol64((*row)[0]);
954  // Int_t dirid = atoi((*row)[1]);
955  Long64_t objid = sqlio::atol64((*row)[2]);
956  const char *keyname = (*row)[3];
957  const char *keytitle = (*row)[4];
958  const char *keydatime = (*row)[5];
959  Int_t cycle = atoi((*row)[6]);
960  const char *classname = (*row)[7];
961 
962  if (gDebug > 4)
963  std::cout << " Reading keyid = " << keyid << " name = " << keyname << std::endl;
964 
965  if ((keyid >= sqlio::Ids_FirstKey) || (keyid == specialkeyid)) {
966  if (doupdate) {
967  TKeySQL *key = FindSQLKey(dir, keyid);
968 
969  if (!key) {
970  Error("StreamKeysForDirectory", "Key with id %lld not exist in list", keyid);
971  nkeys = -1; // this will finish execution
972  } else if (key->IsKeyModified(keyname, keytitle, keydatime, cycle, classname))
973  UpdateKeyData(key);
974 
975  } else {
976  TKeySQL *key = new TKeySQL(dir, keyid, objid, keyname, keytitle, keydatime, cycle, classname);
977  if (specialkey) {
978  *specialkey = key;
979  nkeys = 1;
980  } else
981  dir->GetListOfKeys()->Add(key);
982  }
983  }
984  delete row;
985  }
986 
987  delete res;
988 
989  if (gDebug > 4) {
990  Info("StreamKeysForDirectory", "dir = %s numread = %d", dir->GetName(), nkeys);
991  dir->GetListOfKeys()->Print("*");
992  }
993 
994  return nkeys;
995 }
996 
997 ////////////////////////////////////////////////////////////////////////////////
998 /// initialize sql database and correspondent structures
999 /// identical to TFile::Init() function
1000 
1001 void TSQLFile::InitSqlDatabase(Bool_t create)
1002 {
1003  Int_t len = gROOT->GetListOfStreamerInfo()->GetSize() + 1;
1004  if (len < 5000)
1005  len = 5000;
1006  fClassIndex = new TArrayC(len);
1007  fClassIndex->Reset(0);
1008 
1009  if (!create) {
1010 
1011  Bool_t ok = ReadConfigurations();
1012 
1013  // read data corresponding to TSQLFile
1014  if (ok) {
1015  ReadSQLClassInfos();
1016 
1017  ReadStreamerInfo();
1018 
1019  ok = (ReadSpecialObject(sqlio::Ids_TSQLFile, this) != 0);
1020  }
1021 
1022  // read list of keys
1023  if (ok)
1024  ok = StreamKeysForDirectory(this, kFALSE) >= 0;
1025 
1026  if (!ok) {
1027  Error("InitSqlDatabase", "Cannot detect proper tabled in database. Close.");
1028  Close();
1029  delete fSQL;
1030  fSQL = 0;
1031  MakeZombie();
1032  gDirectory = gROOT;
1033  return;
1034  }
1035  }
1036 
1037  {
1038  R__LOCKGUARD(gROOTMutex);
1039  gROOT->GetListOfFiles()->Add(this);
1040  }
1041  cd();
1042 
1043  fNProcessIDs = 0;
1044  TKey *key = nullptr;
1045  TIter iter(fKeys);
1046  while ((key = (TKey *)iter()) != nullptr) {
1047  if (!strcmp(key->GetClassName(), "TProcessID"))
1048  fNProcessIDs++;
1049  }
1050 
1051  fProcessIDs = new TObjArray(fNProcessIDs + 1);
1052 }
1053 
1054 ////////////////////////////////////////////////////////////////////////////////
1055 /// read table configurations as special table
1056 
1057 Bool_t TSQLFile::ReadConfigurations()
1058 {
1059  const char *quote = SQLIdentifierQuote();
1060 
1061  TString sqlcmd;
1062  sqlcmd.Form("SELECT * FROM %s%s%s", quote, sqlio::ConfigTable, quote);
1063  TSQLResult *res = SQLQuery(sqlcmd.Data(), 2);
1064 
1065  if (!res)
1066  return kFALSE;
1067 
1068  // should be found, otherwise will be error
1069  fSQLIOversion = 0;
1070 
1071  Int_t lock = 0;
1072 
1073 #define ReadIntCfg(name, target) \
1074  if ((field.CompareTo(name, TString::kIgnoreCase) == 0)) \
1075  target = value.Atoi(); \
1076  else
1077 
1078 #define ReadBoolCfg(name, target) \
1079  if ((field.CompareTo(name, TString::kIgnoreCase) == 0)) \
1080  target = value.CompareTo(sqlio::True, TString::kIgnoreCase) == 0; \
1081  else
1082 
1083 #define ReadStrCfg(name, target) \
1084  if ((field.CompareTo(name, TString::kIgnoreCase) == 0)) \
1085  target = value; \
1086  else
1087 
1088  TSQLRow *row = nullptr;
1089 
1090  while ((row = res->Next()) != nullptr) {
1091 
1092  TString field = row->GetField(0);
1093  TString value = row->GetField(1);
1094 
1095  delete row;
1096 
1097  ReadIntCfg(sqlio::cfg_Version, fSQLIOversion) ReadBoolCfg(sqlio::cfg_UseSufixes, fUseSuffixes)
1098  ReadIntCfg(sqlio::cfg_ArrayLimit, fArrayLimit) ReadStrCfg(sqlio::cfg_TablesType, fTablesType)
1099  ReadIntCfg(sqlio::cfg_UseTransactions, fUseTransactions) ReadIntCfg(sqlio::cfg_UseIndexes, fUseIndexes)
1100  ReadIntCfg(sqlio::cfg_ModifyCounter, fModifyCounter) ReadIntCfg(sqlio::cfg_LockingMode, lock)
1101  {
1102  Error("ReadConfigurations", "Invalid configuration field %s", field.Data());
1103  fSQLIOversion = 0;
1104  break;
1105  }
1106  }
1107  (void)lock;
1108 
1109  delete res;
1110 
1111  return (fSQLIOversion > 0);
1112 }
1113 
1114 ////////////////////////////////////////////////////////////////////////////////
1115 /// Creates initial tables in database
1116 /// This is table with configurations and table with keys
1117 /// Function called once when first object is stored to the file.
1118 
1119 void TSQLFile::CreateBasicTables()
1120 {
1121  TString sqlcmd;
1122 
1123  const char *quote = SQLIdentifierQuote();
1124  const char *vquote = SQLValueQuote();
1125 
1126  if (SQLTestTable(sqlio::ConfigTable)) {
1127  sqlcmd.Form("DROP TABLE %s%s%s", quote, sqlio::ConfigTable, quote);
1128  SQLQuery(sqlcmd.Data());
1129  }
1130 
1131  sqlcmd.Form("CREATE TABLE %s%s%s (%s%s%s %s, %s%s%s %s)", quote, sqlio::ConfigTable, quote, quote, sqlio::CT_Field,
1132  quote, SQLSmallTextType(), quote, sqlio::CT_Value, quote, SQLSmallTextType());
1133  if ((fTablesType.Length() > 0) && IsMySQL()) {
1134  sqlcmd += " ENGINE=";
1135  sqlcmd += fTablesType;
1136  }
1137 
1138  SQLQuery(sqlcmd.Data());
1139 
1140 #define WrintCfg(name, type, value) \
1141  { \
1142  sqlcmd.Form("INSERT INTO %s%s%s VALUES (%s%s%s, %s" type "%s)", quote, sqlio::ConfigTable, quote, vquote, name, \
1143  vquote, vquote, value, vquote); \
1144  SQLQuery(sqlcmd.Data()); \
1145  }
1146 
1147  WrintCfg(sqlio::cfg_Version, "%d", fSQLIOversion);
1148  WrintCfg(sqlio::cfg_UseSufixes, "%s", fUseSuffixes ? sqlio::True : sqlio::False);
1149  WrintCfg(sqlio::cfg_ArrayLimit, "%d", fArrayLimit);
1150  WrintCfg(sqlio::cfg_TablesType, "%s", fTablesType.Data());
1151  WrintCfg(sqlio::cfg_UseTransactions, "%d", fUseTransactions);
1152  WrintCfg(sqlio::cfg_UseIndexes, "%d", fUseIndexes);
1153  WrintCfg(sqlio::cfg_ModifyCounter, "%d", fModifyCounter);
1154  WrintCfg(sqlio::cfg_LockingMode, "%d", kLockBusy);
1155 
1156  // from this moment on user cannot change configurations
1157  fCanChangeConfig = kFALSE;
1158 
1159  if (SQLTestTable(sqlio::KeysTable)) {
1160  sqlcmd.Form("DROP TABLE %s%s%s", quote, sqlio::KeysTable, quote);
1161  SQLQuery(sqlcmd.Data());
1162  }
1163 
1164  sqlcmd.Form(
1165  "CREATE TABLE %s%s%s (%s%s%s %s, %s%s%s %s, %s%s%s %s, %s%s%s %s, %s%s%s %s, %s%s%s %s, %s%s%s %s, %s%s%s %s)",
1166  quote, sqlio::KeysTable, quote, quote, SQLKeyIdColumn(), quote, SQLIntType(), quote, SQLDirIdColumn(), quote,
1167  SQLIntType(), quote, SQLObjectIdColumn(), quote, SQLIntType(), quote, sqlio::KT_Name, quote, SQLSmallTextType(),
1168  quote, sqlio::KT_Title, quote, SQLSmallTextType(), quote, sqlio::KT_Datetime, quote, SQLDatetimeType(), quote,
1169  sqlio::KT_Cycle, quote, SQLIntType(), quote, sqlio::KT_Class, quote, SQLSmallTextType());
1170 
1171  if ((fTablesType.Length() > 0) && IsMySQL()) {
1172  sqlcmd += " ENGINE=";
1173  sqlcmd += fTablesType;
1174  }
1175 
1176  SQLQuery(sqlcmd.Data());
1177 
1178  if (GetUseIndexes() > kIndexesNone) {
1179  sqlcmd.Form("CREATE UNIQUE INDEX %s%s%s ON %s%s%s (%s%s%s)", quote, sqlio::KeysTableIndex, quote, quote,
1180  sqlio::KeysTable, quote, quote, SQLKeyIdColumn(), quote);
1181  SQLQuery(sqlcmd.Data());
1182  }
1183 }
1184 
1185 ////////////////////////////////////////////////////////////////////////////////
1186 /// Update value of modify counter in config table
1187 /// Modify counter used to indicate that something was changed in database.
1188 /// It will be used when multiple instances of TSQLFile for the same data base
1189 /// will be connected.
1190 
1191 void TSQLFile::IncrementModifyCounter()
1192 {
1193  if (!IsWritable()) {
1194  Error("IncrementModifyCounter", "Cannot update tables without write accsess");
1195  return;
1196  }
1197 
1198  TString sqlcmd;
1199  const char *quote = SQLIdentifierQuote();
1200  const char *vquote = SQLValueQuote();
1201 
1202  sqlcmd.Form("UPDATE %s%s%s SET %s%s%s=%d WHERE %s%s%s=%s%s%s", quote, sqlio::ConfigTable, quote, quote,
1203  sqlio::CT_Value, quote, ++fModifyCounter, quote, sqlio::CT_Field, quote, vquote,
1204  sqlio::cfg_ModifyCounter, vquote);
1205  SQLQuery(sqlcmd.Data());
1206 }
1207 
1208 ////////////////////////////////////////////////////////////////////////////////
1209 /// Produce \b SELECT statement which can be used to get all data
1210 /// of class cl in one \b SELECT statement.
1211 ///
1212 /// This statement also can be used to create \b VIEW by command like
1213 /// mysql> CREATE VIEW TH1I_view AS $CLASSSELECT$
1214 /// Where \b $CLASSSELECT$ argument should be produced by call
1215 /// f->MakeSelectQuery(TH1I::Class());
1216 /// \b VIEWs supported by latest MySQL 5 and Oracle
1217 
1218 TString TSQLFile::MakeSelectQuery(TClass *cl)
1219 {
1220  TString res = "";
1221  TSQLClassInfo *sqlinfo = FindSQLClassInfo(cl);
1222  if (!sqlinfo)
1223  return res;
1224 
1225  TString columns, tables;
1226  Int_t tablecnt = 0;
1227 
1228  if (!ProduceClassSelectQuery(cl->GetStreamerInfo(), sqlinfo, columns, tables, tablecnt))
1229  return res;
1230 
1231  res.Form("SELECT %s FROM %s", columns.Data(), tables.Data());
1232 
1233  return res;
1234 }
1235 
1236 ////////////////////////////////////////////////////////////////////////////////
1237 /// used by MakeClassSelectQuery method to add columns from table of
1238 /// class, specified by TVirtualStreamerInfo structure
1239 
1240 Bool_t TSQLFile::ProduceClassSelectQuery(TVirtualStreamerInfo *info, TSQLClassInfo *sqlinfo, TString &columns,
1241  TString &tables, Int_t &tablecnt)
1242 {
1243  if (!info || !sqlinfo)
1244  return kFALSE;
1245 
1246  if (!sqlinfo->IsClassTableExist())
1247  return kFALSE;
1248 
1249  const char *quote = SQLIdentifierQuote();
1250 
1251  TString table_syn;
1252  table_syn.Form("t%d", ++tablecnt);
1253 
1254  Bool_t start = (tables.Length() == 0);
1255 
1256  TString buf;
1257 
1258  if (start)
1259  buf.Form("%s AS %s", sqlinfo->GetClassTableName(), table_syn.Data());
1260  else
1261  buf.Form(" LEFT JOIN %s AS %s USING(%s%s%s)", sqlinfo->GetClassTableName(), table_syn.Data(), quote,
1262  SQLObjectIdColumn(), quote);
1263 
1264  tables += buf;
1265 
1266  if (start)
1267  columns.Form("%s.%s%s%s", table_syn.Data(), quote, SQLObjectIdColumn(), quote);
1268 
1269  if (info->GetClass() == TObject::Class()) {
1270  buf.Form(", %s.%s", table_syn.Data(), sqlio::TObjectUniqueId);
1271  columns += buf;
1272  buf.Form(", %s.%s", table_syn.Data(), sqlio::TObjectBits);
1273  columns += buf;
1274  buf.Form(", %s.%s", table_syn.Data(), sqlio::TObjectProcessId);
1275  columns += buf;
1276  return kTRUE;
1277  }
1278 
1279  TIter iter(info->GetElements());
1280  TStreamerElement *elem = nullptr;
1281 
1282  while ((elem = (TStreamerElement *)iter()) != nullptr) {
1283  Int_t coltype = TSQLStructure::DefineElementColumnType(elem, this);
1284  TString colname = TSQLStructure::DefineElementColumnName(elem, this);
1285 
1286  buf = "";
1287  switch (coltype) {
1288 
1289  case TSQLStructure::kColObject:
1290  case TSQLStructure::kColObjectPtr:
1291  case TSQLStructure::kColTString:
1292  case TSQLStructure::kColSimple: {
1293  buf.Form(", %s.%s%s%s", table_syn.Data(), quote, colname.Data(), quote);
1294  columns += buf;
1295  break;
1296  }
1297 
1298  case TSQLStructure::kColParent: {
1299  TClass *parentcl = elem->GetClassPointer();
1300  ProduceClassSelectQuery(parentcl->GetStreamerInfo(), FindSQLClassInfo(parentcl), columns, tables, tablecnt);
1301  break;
1302  }
1303 
1304  case TSQLStructure::kColSimpleArray: {
1305  for (Int_t n = 0; n < elem->GetArrayLength(); n++) {
1306  colname = TSQLStructure::DefineElementColumnName(elem, this, n);
1307  buf.Form(", %s.%s%s%s", table_syn.Data(), quote, colname.Data(), quote);
1308  columns += buf;
1309  }
1310  break;
1311  }
1312  } // switch
1313  }
1314 
1315  return (columns.Length() > 0) && (tables.Length() > 0);
1316 }
1317 
1318 ////////////////////////////////////////////////////////////////////////////////
1319 /// Checks if main keys table is existing
1320 
1321 Bool_t TSQLFile::IsTablesExists()
1322 {
1323  return SQLTestTable(sqlio::KeysTable) && SQLTestTable(sqlio::ConfigTable);
1324 }
1325 
1326 ////////////////////////////////////////////////////////////////////////////////
1327 /// Checkis, if lock is free in configuration tables
1328 
1329 Bool_t TSQLFile::IsWriteAccess()
1330 {
1331  return GetLocking() == kLockFree;
1332 }
1333 
1334 ////////////////////////////////////////////////////////////////////////////////
1335 /// Set locking mode for current database
1336 
1337 void TSQLFile::SetLocking(Int_t mode)
1338 {
1339  TString sqlcmd;
1340  const char *quote = SQLIdentifierQuote();
1341  const char *vquote = SQLValueQuote();
1342 
1343  sqlcmd.Form("UPDATE %s%s%s SET %s%s%s=%d WHERE %s%s%s=%s%s%s", quote, sqlio::ConfigTable, quote, quote,
1344  sqlio::CT_Value, quote, mode, quote, sqlio::CT_Field, quote, vquote, sqlio::cfg_LockingMode, vquote);
1345  SQLQuery(sqlcmd.Data());
1346 }
1347 
1348 ////////////////////////////////////////////////////////////////////////////////
1349 /// Return current locking mode for that file
1350 
1351 Int_t TSQLFile::GetLocking()
1352 {
1353  const char *quote = SQLIdentifierQuote();
1354  const char *vquote = SQLValueQuote();
1355 
1356  TString sqlcmd;
1357  sqlcmd.Form("SELECT %s%s%s FROM %s%s%s WHERE %s%s%s=%s%s%s", quote, sqlio::CT_Value, quote, quote,
1358  sqlio::ConfigTable, quote, quote, sqlio::CT_Field, quote, vquote, sqlio::cfg_LockingMode, vquote);
1359 
1360  TSQLResult *res = SQLQuery(sqlcmd.Data(), 1);
1361  TSQLRow *row = (res == 0) ? 0 : res->Next();
1362  TString field = (row == 0) ? "" : row->GetField(0);
1363  delete row;
1364  delete res;
1365 
1366  if (field.Length() == 0)
1367  return kLockFree;
1368 
1369  return field.Atoi();
1370 }
1371 
1372 ////////////////////////////////////////////////////////////////////////////////
1373 /// dummy, in future should check about read access to database
1374 
1375 Bool_t TSQLFile::IsReadAccess()
1376 {
1377  return kTRUE;
1378 }
1379 
1380 ////////////////////////////////////////////////////////////////////////////////
1381 /// Submits query to SQL server.
1382 ///
1383 /// | Flag Value | Effect|
1384 /// |------------|-------|
1385 /// | 0 | result is not interesting and will be deleted|
1386 /// | 1 | return result of submitted query
1387 /// | 2 | results is may be necessary for long time Oracle plugin do not support working with several TSQLResult
1388 /// objects, therefore explicit deep copy will be produced|
1389 ///
1390 /// If ok!=0, it will contains kTRUE is Query was successful, otherwise kFALSE
1391 
1392 TSQLResult *TSQLFile::SQLQuery(const char *cmd, Int_t flag, Bool_t *ok)
1393 {
1394  if (fLogFile)
1395  *fLogFile << cmd << std::endl;
1396 
1397  if (ok)
1398  *ok = kFALSE;
1399 
1400  if (!fSQL)
1401  return nullptr;
1402 
1403  if (gDebug > 2)
1404  Info("SQLQuery", "%s", cmd);
1405 
1406  fQuerisCounter++;
1407 
1408  if (flag == 0) {
1409  Bool_t res = fSQL->Exec(cmd);
1410  if (ok)
1411  *ok = res;
1412  return nullptr;
1413  }
1414 
1415  TSQLResult *res = fSQL->Query(cmd);
1416  if (ok)
1417  *ok = res != 0;
1418  if (!res)
1419  return nullptr;
1420  // if ((flag==2) && IsOracle())
1421  // res = new TSQLResultCopy(res);
1422  return res;
1423 }
1424 
1425 ////////////////////////////////////////////////////////////////////////////////
1426 /// Test if DB support statement and number of open statements is not exceeded
1427 
1428 Bool_t TSQLFile::SQLCanStatement()
1429 {
1430  if (!fSQL)
1431  return kFALSE;
1432 
1433  if (!fSQL->HasStatement())
1434  return kFALSE;
1435 
1436  return kTRUE; // !IsOracle() || (fStmtCounter<15);
1437 }
1438 
1439 ////////////////////////////////////////////////////////////////////////////////
1440 /// Produces SQL statement for currently conected DB server
1441 
1442 TSQLStatement *TSQLFile::SQLStatement(const char *cmd, Int_t bufsize)
1443 {
1444  if (!fSQL)
1445  return nullptr;
1446 
1447  if (!fSQL->HasStatement())
1448  return 0;
1449 
1450  if (gDebug > 1)
1451  Info("SQLStatement", "%s", cmd);
1452 
1453  fStmtCounter++;
1454  fQuerisCounter++; // one statement counts as one query
1455 
1456  return fSQL->Statement(cmd, bufsize);
1457 }
1458 
1459 ////////////////////////////////////////////////////////////////////////////////
1460 /// delete statement and decrease counter
1461 
1462 void TSQLFile::SQLDeleteStatement(TSQLStatement *stmt)
1463 {
1464  if (!stmt)
1465  return;
1466 
1467  fStmtCounter--;
1468 
1469  delete stmt;
1470 }
1471 
1472 ////////////////////////////////////////////////////////////////////////////////
1473 /// supplies set of commands to server
1474 /// Commands is stored as array of TObjString
1475 
1476 Bool_t TSQLFile::SQLApplyCommands(TObjArray *cmds)
1477 {
1478  if (!cmds || !fSQL)
1479  return kFALSE;
1480 
1481  Bool_t ok = kTRUE;
1482  TIter iter(cmds);
1483  TObject *cmd = nullptr;
1484  while ((cmd = iter()) != nullptr) {
1485  SQLQuery(cmd->GetName(), 0, &ok);
1486  if (!ok)
1487  break;
1488  }
1489 
1490  return ok;
1491 }
1492 
1493 ////////////////////////////////////////////////////////////////////////////////
1494 /// Test, if table of specified name exists
1495 
1496 Bool_t TSQLFile::SQLTestTable(const char *tablename)
1497 {
1498  if (!fSQL)
1499  return kFALSE;
1500 
1501  if (fSQL->HasTable(tablename))
1502  return kTRUE;
1503 
1504  TString buf(tablename);
1505  buf.ToLower();
1506  if (fSQL->HasTable(buf.Data()))
1507  return kTRUE;
1508  buf.ToUpper();
1509  return fSQL->HasTable(buf.Data());
1510 }
1511 
1512 ////////////////////////////////////////////////////////////////////////////////
1513 /// Returns maximum value, found in specified columnname of table tablename
1514 /// Column type should be numeric
1515 
1516 Long64_t TSQLFile::SQLMaximumValue(const char *tablename, const char *columnname)
1517 {
1518  if (!fSQL)
1519  return -1;
1520 
1521  if (gDebug > 2)
1522  Info("SQLMaximumValue", "Requests for %s column %s", tablename, columnname);
1523 
1524  const char *quote = SQLIdentifierQuote();
1525 
1526  TString query;
1527  query.Form("SELECT MAX(%s%s%s) FROM %s%s%s", quote, columnname, quote, quote, tablename, quote);
1528  TSQLResult *res = SQLQuery(query.Data(), 1);
1529 
1530  if (!res)
1531  return -1;
1532 
1533  TSQLRow *row = res->Next();
1534 
1535  Long64_t maxid = -1;
1536  if (row != 0)
1537  if (row->GetField(0) != 0)
1538  maxid = sqlio::atol64(row->GetField(0));
1539 
1540  delete row;
1541  delete res;
1542 
1543  if (gDebug > 2)
1544  Info("SQLMaximumValue", "Result = %lld", maxid);
1545 
1546  return maxid;
1547 }
1548 
1549 ////////////////////////////////////////////////////////////////////////////////
1550 /// Delete all tables in database
1551 
1552 void TSQLFile::SQLDeleteAllTables()
1553 {
1554  if (!fSQL)
1555  return;
1556 
1557  TList *tables = fSQL->GetTablesList();
1558  if (!tables)
1559  return;
1560 
1561  TString sqlcmd;
1562  const char *quote = SQLIdentifierQuote();
1563 
1564  TIter iter(tables);
1565  TObject *obj = nullptr;
1566  while ((obj = iter()) != nullptr) {
1567  sqlcmd.Form("DROP TABLE %s%s%s", quote, obj->GetName(), quote);
1568  SQLQuery(sqlcmd.Data());
1569  }
1570  delete tables;
1571 }
1572 
1573 ////////////////////////////////////////////////////////////////////////////////
1574 /// Start SQL transaction.
1575 
1576 Bool_t TSQLFile::SQLStartTransaction()
1577 {
1578  return fSQL ? fSQL->StartTransaction() : kFALSE;
1579 }
1580 
1581 ////////////////////////////////////////////////////////////////////////////////
1582 /// Commit SQL transaction
1583 
1584 Bool_t TSQLFile::SQLCommit()
1585 {
1586  return fSQL ? fSQL->Commit() : kFALSE;
1587 }
1588 
1589 ////////////////////////////////////////////////////////////////////////////////
1590 /// Rollback all SQL operations, done after start transaction
1591 
1592 Bool_t TSQLFile::SQLRollback()
1593 {
1594  return fSQL ? fSQL->Rollback() : kFALSE;
1595 }
1596 
1597 ////////////////////////////////////////////////////////////////////////////////
1598 /// returns maximum allowed length of identifiers
1599 
1600 Int_t TSQLFile::SQLMaxIdentifierLength()
1601 {
1602  Int_t maxlen = !fSQL ? 32 : fSQL->GetMaxIdentifierLength();
1603 
1604  // lets exclude absolute ubnormal data
1605  if (maxlen < 10)
1606  maxlen = 10;
1607 
1608  return maxlen;
1609 }
1610 
1611 ////////////////////////////////////////////////////////////////////////////////
1612 /// Remove key with specified id from keys table
1613 /// also removes all objects data, related to this table
1614 
1615 void TSQLFile::DeleteKeyFromDB(Long64_t keyid)
1616 {
1617  if (!IsWritable() || (keyid < 0) || !fSQL)
1618  return;
1619 
1620  TString sqlcmd;
1621  const char *quote = SQLIdentifierQuote();
1622 
1623  sqlcmd.Form("SELECT MIN(%s%s%s), MAX(%s%s%s) FROM %s%s%s WHERE %s%s%s=%lld", quote, SQLObjectIdColumn(), quote,
1624  quote, SQLObjectIdColumn(), quote, quote, sqlio::ObjectsTable, quote, quote, SQLKeyIdColumn(), quote,
1625  keyid);
1626  TSQLResult *res = SQLQuery(sqlcmd.Data(), 2);
1627  TSQLRow *row = res == 0 ? 0 : res->Next();
1628  Long64_t minid(1), maxid(0);
1629 
1630  if (row && (row->GetField(0) != 0) && (row->GetField(1) != 0)) {
1631  minid = sqlio::atol64(row->GetField(0));
1632  maxid = sqlio::atol64(row->GetField(1));
1633  }
1634 
1635  delete row;
1636  delete res;
1637 
1638  // can be that object tables does not include any entry this that keyid
1639  if (minid <= maxid) {
1640  TIter iter(fSQLClassInfos);
1641  TSQLClassInfo *info = nullptr;
1642  TString querymask, query;
1643  querymask.Form("DELETE FROM %s%s%s WHERE %s%s%s BETWEEN %lld AND %lld", quote, "%s", quote, quote,
1644  SQLObjectIdColumn(), quote, minid, maxid);
1645 
1646  while ((info = (TSQLClassInfo *)iter()) != nullptr) {
1647 
1648  if (info->IsClassTableExist()) {
1649  query.Form(querymask.Data(), info->GetClassTableName());
1650  SQLQuery(query.Data());
1651  }
1652 
1653  if (info->IsRawTableExist()) {
1654  query.Form(querymask.Data(), info->GetRawTableName());
1655  SQLQuery(query.Data());
1656  }
1657  }
1658  }
1659 
1660  sqlcmd.Form("DELETE FROM %s%s%s WHERE %s%s%s=%lld", quote, sqlio::ObjectsTable, quote, quote, SQLKeyIdColumn(),
1661  quote, keyid);
1662  SQLQuery(sqlcmd.Data());
1663 
1664  sqlcmd.Form("DELETE FROM %s%s%s WHERE %s%s%s=%lld", quote, sqlio::KeysTable, quote, quote, SQLKeyIdColumn(), quote,
1665  keyid);
1666  SQLQuery(sqlcmd.Data());
1667 
1668  IncrementModifyCounter();
1669 }
1670 
1671 ////////////////////////////////////////////////////////////////////////////////
1672 /// Search for TKeySQL object with specified keyid
1673 
1674 TKeySQL *TSQLFile::FindSQLKey(TDirectory *dir, Long64_t keyid)
1675 {
1676  if (!dir)
1677  return nullptr;
1678 
1679  TIter next(dir->GetListOfKeys());
1680  TObject *obj = nullptr;
1681 
1682  while ((obj = next()) != nullptr) {
1683  TKeySQL *key = dynamic_cast<TKeySQL *>(obj);
1684  if (key && (key->GetDBKeyId() == keyid))
1685  return key;
1686  }
1687 
1688  return nullptr;
1689 }
1690 
1691 ////////////////////////////////////////////////////////////////////////////////
1692 /// Add entry into keys table
1693 
1694 Bool_t TSQLFile::WriteKeyData(TKeySQL *key)
1695 {
1696  if (!fSQL || !key)
1697  return kFALSE;
1698 
1699  if (!IsTablesExists())
1700  CreateBasicTables();
1701 
1702  TString sqlcmd;
1703  const char *valuequote = SQLValueQuote();
1704  const char *quote = SQLIdentifierQuote();
1705 
1706  sqlcmd.Form("INSERT INTO %s%s%s VALUES (%lld, %lld, %lld, %s%s%s, %s%s%s, %s%s%s, %d, %s%s%s)", quote,
1707  sqlio::KeysTable, quote, key->GetDBKeyId(), key->GetDBDirId(), key->GetDBObjId(), valuequote,
1708  key->GetName(), valuequote, valuequote, key->GetTitle(), valuequote,
1709  valuequote, TestBit(TFile::kReproducible) ? TDatime((UInt_t) 1).AsSQLString() : key->GetDatime().AsSQLString(), valuequote,
1710  key->GetCycle(), valuequote, key->GetClassName(), valuequote);
1711 
1712  Bool_t ok = kTRUE;
1713 
1714  SQLQuery(sqlcmd.Data(), 0, &ok);
1715 
1716  if (ok)
1717  IncrementModifyCounter();
1718 
1719  return ok;
1720 }
1721 
1722 ////////////////////////////////////////////////////////////////////////////////
1723 /// Updates (overwrites) key data in KeysTable
1724 
1725 Bool_t TSQLFile::UpdateKeyData(TKeySQL *key)
1726 {
1727  if (!fSQL || !key)
1728  return kFALSE;
1729 
1730  TString sqlcmd;
1731  const char *valuequote = SQLValueQuote();
1732  const char *quote = SQLIdentifierQuote();
1733 
1734  TString keyname = key->GetName();
1735  TString keytitle = key->GetTitle();
1736  TString keydatime = key->GetDatime().AsSQLString();
1737 
1738  TSQLStructure::AddStrBrackets(keyname, valuequote);
1739  TSQLStructure::AddStrBrackets(keytitle, valuequote);
1740  TSQLStructure::AddStrBrackets(keydatime, valuequote);
1741 
1742  sqlcmd.Form("UPDATE %s%s%s SET %s%s%s=%s, %s%s%s=%s, %s%s%s=%s, %s%s%s=%d WHERE %s%s%s=%lld", quote,
1743  sqlio::KeysTable, quote, quote, sqlio::KT_Name, quote, keyname.Data(), quote, sqlio::KT_Title, quote,
1744  keytitle.Data(), quote, sqlio::KT_Datetime, quote, keydatime.Data(), quote, sqlio::KT_Cycle, quote,
1745  key->GetCycle(), quote, SQLKeyIdColumn(), quote, key->GetDBKeyId());
1746 
1747  Bool_t ok = kTRUE;
1748 
1749  SQLQuery(sqlcmd.Data(), 0, &ok);
1750 
1751  if (ok)
1752  IncrementModifyCounter();
1753 
1754  return ok;
1755 }
1756 
1757 ////////////////////////////////////////////////////////////////////////////////
1758 /// Returns next possible key identifier
1759 
1760 Long64_t TSQLFile::DefineNextKeyId()
1761 {
1762  Long64_t max = -1;
1763 
1764  if (SQLTestTable(sqlio::KeysTable))
1765  max = SQLMaximumValue(sqlio::KeysTable, SQLKeyIdColumn());
1766 
1767  if (max < 0)
1768  return sqlio::Ids_FirstKey;
1769 
1770  return max + 1;
1771 }
1772 
1773 ////////////////////////////////////////////////////////////////////////////////
1774 /// Return (if exists) TSQLClassInfo for specified class name and version
1775 
1776 TSQLClassInfo *TSQLFile::FindSQLClassInfo(const char *clname, Int_t version)
1777 {
1778  if (!fSQLClassInfos)
1779  return nullptr;
1780 
1781  TIter iter(fSQLClassInfos);
1782  TSQLClassInfo *info = nullptr;
1783 
1784  while ((info = (TSQLClassInfo *)iter()) != nullptr) {
1785  if (strcmp(info->GetName(), clname) == 0)
1786  if (info->GetClassVersion() == version)
1787  return info;
1788  }
1789  return nullptr;
1790 }
1791 
1792 ////////////////////////////////////////////////////////////////////////////////
1793 /// return (if exists) TSQLClassInfo for specified class
1794 
1795 TSQLClassInfo *TSQLFile::FindSQLClassInfo(const TClass *cl)
1796 {
1797  return FindSQLClassInfo(cl->GetName(), cl->GetClassVersion());
1798 }
1799 
1800 ////////////////////////////////////////////////////////////////////////////////
1801 /// Search in database tables for specified class and return TSQLClassInfo object
1802 
1803 TSQLClassInfo *TSQLFile::RequestSQLClassInfo(const char *clname, Int_t version)
1804 {
1805  TSQLClassInfo *info = FindSQLClassInfo(clname, version);
1806  if (info)
1807  return info;
1808 
1809  if (!fSQL)
1810  return nullptr;
1811 
1812  Long64_t maxid = 0;
1813 
1814  if (fSQLClassInfos) {
1815  TIter iter(fSQLClassInfos);
1816  while ((info = (TSQLClassInfo *)iter()) != nullptr) {
1817  if (info->GetClassId() > maxid)
1818  maxid = info->GetClassId();
1819  }
1820  }
1821 
1822  info = new TSQLClassInfo(maxid + 1, clname, version);
1823 
1824  info->SetClassTableName(DefineTableName(clname, version, kFALSE));
1825  info->SetRawTableName(DefineTableName(clname, version, kTRUE));
1826 
1827  if (!fSQLClassInfos)
1828  fSQLClassInfos = new TList;
1829  fSQLClassInfos->Add(info);
1830 
1831  return info;
1832 }
1833 
1834 ////////////////////////////////////////////////////////////////////////////////
1835 /// Proposes table name for class
1836 
1837 TString TSQLFile::DefineTableName(const char *clname, Int_t version, Bool_t rawtable)
1838 {
1839  Int_t maxlen = SQLMaxIdentifierLength();
1840 
1841  TString res;
1842 
1843  const char *suffix = rawtable ? "_raw" : "_ver";
1844 
1845  res.Form("%s%s%d", clname, suffix, version);
1846 
1847  if ((res.Length() <= maxlen) && !HasTable(res.Data()))
1848  return res;
1849 
1850  TString scnt;
1851 
1852  Int_t len = strlen(clname);
1853  Int_t cnt = version;
1854  if (cnt > 100)
1855  cnt = 0; // do not start with the biggest values
1856 
1857  do {
1858  scnt.Form("%d%s", cnt, suffix);
1859  Int_t numlen = scnt.Length();
1860  if (numlen >= maxlen - 2)
1861  break;
1862 
1863  res = clname;
1864 
1865  if (len + numlen > maxlen)
1866  res.Resize(maxlen - numlen);
1867 
1868  res += scnt;
1869 
1870  if (!HasTable(res.Data()))
1871  return res;
1872 
1873  cnt++;
1874 
1875  } while (cnt < 10000);
1876 
1877  Error("DefineTableName", "Cannot produce table name for class %s ver %d", clname, version);
1878  res.Form("%s%s%d", clname, suffix, version);
1879 
1880  return res;
1881 }
1882 
1883 ////////////////////////////////////////////////////////////////////////////////
1884 /// Test if table name exists
1885 
1886 Bool_t TSQLFile::HasTable(const char *name)
1887 {
1888  if (!fSQLClassInfos)
1889  return kFALSE;
1890 
1891  TIter iter(fSQLClassInfos);
1892  TSQLClassInfo *info = nullptr;
1893  while ((info = (TSQLClassInfo *)iter()) != nullptr) {
1894  if (strcmp(info->GetClassTableName(), name) == 0)
1895  return kTRUE;
1896  if (strcmp(info->GetRawTableName(), name) == 0)
1897  return kTRUE;
1898  }
1899 
1900  return kFALSE;
1901 }
1902 
1903 ////////////////////////////////////////////////////////////////////////////////
1904 /// Search in database tables for specified class and return TSQLClassInfo object
1905 
1906 TSQLClassInfo *TSQLFile::RequestSQLClassInfo(const TClass *cl)
1907 {
1908  return RequestSQLClassInfo(cl->GetName(), cl->GetClassVersion());
1909 }
1910 
1911 ////////////////////////////////////////////////////////////////////////////////
1912 /// Read all class infos from IdsTable
1913 
1914 void TSQLFile::ReadSQLClassInfos()
1915 {
1916  if (!fSQL)
1917  return;
1918 
1919  fIdsTableExists = SQLTestTable(sqlio::IdsTable);
1920 
1921  if (!fIdsTableExists)
1922  return;
1923 
1924  TString sqlcmd;
1925  const char *quote = SQLIdentifierQuote();
1926 
1927  sqlcmd.Form("SELECT * FROM %s%s%s WHERE %s%s%s = %d ORDER BY %s%s%s", quote, sqlio::IdsTable, quote, quote,
1928  sqlio::IT_Type, quote, TSQLStructure::kIdTable, quote, sqlio::IT_TableID, quote);
1929 
1930  TSQLResult *res = SQLQuery(sqlcmd.Data(), 1);
1931 
1932  TSQLRow *row = nullptr;
1933 
1934  if (res)
1935  while ((row = res->Next()) != nullptr) {
1936  Long64_t tableid = sqlio::atol64(row->GetField(0));
1937  Int_t version = atoi(row->GetField(1));
1938 
1939  const char *classname = row->GetField(3);
1940  const char *classtable = row->GetField(4);
1941 
1942  TSQLClassInfo *info = new TSQLClassInfo(tableid, classname, version);
1943  info->SetClassTableName(classtable);
1944 
1945  if (!fSQLClassInfos)
1946  fSQLClassInfos = new TList;
1947  fSQLClassInfos->Add(info);
1948 
1949  delete row;
1950  }
1951  delete res;
1952 
1953  TIter next(fSQLClassInfos);
1954  TSQLClassInfo *info = nullptr;
1955 
1956  while ((info = (TSQLClassInfo *)next()) != nullptr) {
1957  sqlcmd.Form("SELECT * FROM %s%s%s WHERE %s%s%s = %lld ORDER BY %s%s%s", quote, sqlio::IdsTable, quote, quote,
1958  sqlio::IT_TableID, quote, info->GetClassId(), quote, sqlio::IT_SubID, quote);
1959  res = SQLQuery(sqlcmd.Data(), 1);
1960 
1961  TObjArray *cols = nullptr;
1962 
1963  if (res)
1964  while ((row = res->Next()) != nullptr) {
1965 
1966  Int_t typ = atoi(row->GetField(2));
1967 
1968  const char *fullname = row->GetField(3);
1969  const char *sqlname = row->GetField(4);
1970  const char *info2 = row->GetField(5);
1971 
1972  if (typ == TSQLStructure::kIdColumn) {
1973  if (!cols)
1974  cols = new TObjArray;
1975  cols->Add(new TSQLClassColumnInfo(fullname, sqlname, info2));
1976  }
1977 
1978  delete row;
1979  }
1980 
1981  delete res;
1982 
1983  info->SetColumns(cols);
1984  }
1985 
1986  sqlcmd.Form("SELECT * FROM %s%s%s WHERE %s%s%s = %d ORDER BY %s%s%s", quote, sqlio::IdsTable, quote, quote,
1987  sqlio::IT_Type, quote, TSQLStructure::kIdRawTable, quote, sqlio::IT_TableID, quote);
1988 
1989  res = SQLQuery(sqlcmd.Data(), 1);
1990 
1991  if (res)
1992  while ((row = res->Next()) != nullptr) {
1993  Long64_t tableid = sqlio::atol64(row->GetField(0));
1994  Int_t version = atoi(row->GetField(1));
1995 
1996  const char *classname = row->GetField(3);
1997  const char *rawtable = row->GetField(4);
1998 
1999  TSQLClassInfo *info2 = FindSQLClassInfo(classname, version);
2000 
2001  if (!info2) {
2002  info2 = new TSQLClassInfo(tableid, classname, version);
2003 
2004  if (!fSQLClassInfos)
2005  fSQLClassInfos = new TList;
2006  fSQLClassInfos->Add(info2);
2007  }
2008 
2009  info2->SetRawTableName(rawtable);
2010  info2->SetRawExist(kTRUE);
2011 
2012  delete row;
2013  }
2014 
2015  delete res;
2016 }
2017 
2018 ////////////////////////////////////////////////////////////////////////////////
2019 /// Add entry into IdsTable, where all tables names and columns names are listed
2020 
2021 void TSQLFile::AddIdEntry(Long64_t tableid, Int_t subid, Int_t type, const char *name, const char *sqlname,
2022  const char *info)
2023 {
2024  if (!fSQL || !IsWritable())
2025  return;
2026 
2027  TString sqlcmd;
2028  const char *valuequote = SQLValueQuote();
2029  const char *quote = SQLIdentifierQuote();
2030 
2031  if (!fIdsTableExists) {
2032 
2033  if (SQLTestTable(sqlio::IdsTable)) {
2034  sqlcmd.Form("DROP TABLE %s%s%s", quote, sqlio::IdsTable, quote);
2035  SQLQuery(sqlcmd.Data());
2036  }
2037 
2038  sqlcmd.Form("CREATE TABLE %s%s%s (%s%s%s %s, %s%s%s %s, %s%s%s %s, %s%s%s %s, %s%s%s %s, %s%s%s %s)", quote,
2039  sqlio::IdsTable, quote, quote, sqlio::IT_TableID, quote, SQLIntType(), quote, sqlio::IT_SubID, quote,
2040  SQLIntType(), quote, sqlio::IT_Type, quote, SQLIntType(), quote, sqlio::IT_FullName, quote,
2041  SQLSmallTextType(), quote, sqlio::IT_SQLName, quote, SQLSmallTextType(), quote, sqlio::IT_Info, quote,
2042  SQLSmallTextType());
2043  if ((fTablesType.Length() > 0) && IsMySQL()) {
2044  sqlcmd += " ENGINE=";
2045  sqlcmd += fTablesType;
2046  }
2047  SQLQuery(sqlcmd.Data());
2048 
2049  fIdsTableExists = kTRUE;
2050  }
2051 
2052  sqlcmd.Form("INSERT INTO %s%s%s VALUES (%lld, %d, %d, %s%s%s, %s%s%s, %s%s%s)", quote, sqlio::IdsTable, quote,
2053  tableid, subid, type, valuequote, name, valuequote, valuequote, sqlname, valuequote, valuequote, info,
2054  valuequote);
2055 
2056  SQLQuery(sqlcmd.Data());
2057 }
2058 
2059 ////////////////////////////////////////////////////////////////////////////////
2060 /// Create normal class table if required
2061 
2062 Bool_t TSQLFile::CreateClassTable(TSQLClassInfo *sqlinfo, TObjArray *colinfos)
2063 {
2064  if (!sqlinfo)
2065  return kFALSE;
2066 
2067  // this is normal situation, when no extra column infos was created when not necessary
2068  if (!colinfos)
2069  return sqlinfo->IsClassTableExist();
2070 
2071  if (sqlinfo->IsClassTableExist()) {
2072  if (colinfos) {
2073  colinfos->Delete();
2074  delete colinfos;
2075  // Error("CreateClassTable","Why colinfos for table %s", sqlinfo->GetClassTableName());
2076  }
2077  return kTRUE;
2078  }
2079 
2080  if (gDebug > 2)
2081  Info("CreateClassTable", "cl:%s", sqlinfo->GetName());
2082 
2083  const char *quote = SQLIdentifierQuote();
2084 
2085  AddIdEntry(sqlinfo->GetClassId(), sqlinfo->GetClassVersion(), TSQLStructure::kIdTable, sqlinfo->GetName(),
2086  sqlinfo->GetClassTableName(), "Main class table");
2087 
2088  TString sqlcmd;
2089  sqlcmd.Form("CREATE TABLE %s%s%s (", quote, sqlinfo->GetClassTableName(), quote);
2090 
2091  TIter iter(colinfos);
2092  TSQLClassColumnInfo *col;
2093  Bool_t first = kTRUE;
2094  Bool_t forcequote = IsOracle();
2095  Int_t colid = 0;
2096  while ((col = (TSQLClassColumnInfo *)iter()) != nullptr) {
2097  if (!first)
2098  sqlcmd += ", ";
2099  else
2100  first = false;
2101 
2102  const char *colname = col->GetSQLName();
2103  if ((strpbrk(colname, "[:.]<>") != 0) || forcequote) {
2104  sqlcmd += quote;
2105  sqlcmd += colname;
2106  sqlcmd += quote;
2107  sqlcmd += " ";
2108  } else {
2109  sqlcmd += colname, sqlcmd += " ";
2110  }
2111 
2112  sqlcmd += col->GetSQLType();
2113 
2114  AddIdEntry(sqlinfo->GetClassId(), colid++, TSQLStructure::kIdColumn, col->GetName(), col->GetSQLName(),
2115  col->GetSQLType());
2116  }
2117  sqlcmd += ")";
2118 
2119  if ((fTablesType.Length() > 0) && IsMySQL()) {
2120  sqlcmd += " ENGINE=";
2121  sqlcmd += fTablesType;
2122  }
2123 
2124  SQLQuery(sqlcmd.Data());
2125 
2126  sqlinfo->SetColumns(colinfos);
2127 
2128  if (GetUseIndexes() > kIndexesBasic) {
2129 
2130  TString indxname = sqlinfo->GetClassTableName();
2131  indxname.ReplaceAll("_ver", "_i1x");
2132 
2133  sqlcmd.Form("CREATE UNIQUE INDEX %s%s_I1%s ON %s%s%s (%s%s%s)", quote, indxname.Data(), quote, quote,
2134  sqlinfo->GetClassTableName(), quote, quote, SQLObjectIdColumn(), quote);
2135  SQLQuery(sqlcmd.Data());
2136  }
2137 
2138  return kTRUE;
2139 }
2140 
2141 ////////////////////////////////////////////////////////////////////////////////
2142 /// Create the raw table
2143 
2144 Bool_t TSQLFile::CreateRawTable(TSQLClassInfo *sqlinfo)
2145 {
2146  if (!sqlinfo)
2147  return kFALSE;
2148 
2149  if (sqlinfo->IsRawTableExist())
2150  return kTRUE;
2151 
2152  const char *quote = SQLIdentifierQuote();
2153 
2154  if (gDebug > 2)
2155  Info("CreateRawTable", "%s", sqlinfo->GetName());
2156 
2157  TString sqlcmd;
2158 
2159  sqlcmd.Form("CREATE TABLE %s%s%s (%s%s%s %s, %s%s%s %s, %s %s, %s %s)", quote, sqlinfo->GetRawTableName(), quote,
2160  quote, SQLObjectIdColumn(), quote, SQLIntType(), quote, SQLRawIdColumn(), quote, SQLIntType(),
2161  sqlio::BT_Field, SQLSmallTextType(), sqlio::BT_Value, SQLSmallTextType());
2162 
2163  if ((fTablesType.Length() > 0) && IsMySQL()) {
2164  sqlcmd += " ENGINE=";
2165  sqlcmd += fTablesType;
2166  }
2167 
2168  SQLQuery(sqlcmd.Data());
2169  sqlinfo->SetRawExist(kTRUE);
2170 
2171  if (GetUseIndexes() > kIndexesClass) {
2172  TString indxname = sqlinfo->GetClassTableName();
2173  indxname.ReplaceAll("_ver", "_i2x");
2174 
2175  sqlcmd.Form("CREATE UNIQUE INDEX %s%s_I2%s ON %s%s%s (%s%s%s, %s%s%s)", quote, indxname.Data(), quote, quote,
2176  sqlinfo->GetRawTableName(), quote, quote, SQLObjectIdColumn(), quote, quote, SQLRawIdColumn(), quote);
2177  SQLQuery(sqlcmd.Data());
2178  }
2179 
2180  AddIdEntry(sqlinfo->GetClassId(), sqlinfo->GetClassVersion(), TSQLStructure::kIdRawTable, sqlinfo->GetName(),
2181  sqlinfo->GetRawTableName(), "Raw data class table");
2182 
2183  return kTRUE;
2184 }
2185 
2186 ////////////////////////////////////////////////////////////////////////////////
2187 /// Checks that table for big strings is exists
2188 /// If not, will be created
2189 
2190 Bool_t TSQLFile::VerifyLongStringTable()
2191 {
2192  if (!fSQL)
2193  return kFALSE;
2194 
2195  if (SQLTestTable(sqlio::StringsTable))
2196  return kTRUE;
2197 
2198  const char *quote = SQLIdentifierQuote();
2199 
2200  TString sqlcmd;
2201  sqlcmd.Form("CREATE TABLE %s (%s%s%s %s, %s%s%s %s, %s %s)", sqlio::StringsTable, quote, SQLObjectIdColumn(), quote,
2202  SQLIntType(), quote, SQLStrIdColumn(), quote, SQLIntType(), sqlio::ST_Value, SQLBigTextType());
2203 
2204  if (fTablesType.Length() > 0) {
2205  sqlcmd += " ENGINE=";
2206  sqlcmd += fTablesType;
2207  }
2208 
2209  SQLQuery(sqlcmd.Data());
2210 
2211  return kTRUE;
2212 }
2213 
2214 ////////////////////////////////////////////////////////////////////////////////
2215 /// Produces id which will be placed in column instead of string itself
2216 
2217 TString TSQLFile::CodeLongString(Long64_t objid, Int_t strid)
2218 {
2219  TString res;
2220  res.Form("%s %lld %s %d %s", sqlio::LongStrPrefix, objid, sqlio::LongStrPrefix, strid, sqlio::LongStrPrefix);
2221  return res;
2222 }
2223 
2224 ////////////////////////////////////////////////////////////////////////////////
2225 /// Checks if this is long string code
2226 /// returns 0, if not or string id
2227 
2228 Int_t TSQLFile::IsLongStringCode(Long64_t objid, const char *value)
2229 {
2230  if (!value)
2231  return 0;
2232  if (strlen(value) < strlen(sqlio::LongStrPrefix) * 3 + 6)
2233  return 0;
2234  if (strstr(value, sqlio::LongStrPrefix) != value)
2235  return 0;
2236 
2237  value += strlen(sqlio::LongStrPrefix);
2238  if (*value++ != ' ')
2239  return 0;
2240  TString s_strid, s_objid;
2241  if ((*value < '1') || (*value > '9'))
2242  return 0;
2243  do {
2244  s_objid.Append(*value++);
2245  } while ((*value != 0) && (*value >= '0') && (*value <= '9'));
2246 
2247  if (*value++ != ' ')
2248  return 0;
2249  if ((*value == 0) || (strstr(value, sqlio::LongStrPrefix) != value))
2250  return 0;
2251  value += strlen(sqlio::LongStrPrefix);
2252  if (*value++ != ' ')
2253  return 0;
2254 
2255  if ((*value < '1') || (*value > '9'))
2256  return 0;
2257  do {
2258  s_strid.Append(*value++);
2259  } while ((*value != 0) && (*value >= '0') && (*value <= '9'));
2260  if (*value++ != ' ')
2261  return 0;
2262 
2263  if ((*value == 0) || (strcmp(value, sqlio::LongStrPrefix) != 0))
2264  return 0;
2265 
2266  Long64_t objid2 = sqlio::atol64(s_objid.Data());
2267  if (objid2 != objid)
2268  return 0;
2269 
2270  return atoi(s_strid.Data());
2271 }
2272 
2273 ////////////////////////////////////////////////////////////////////////////////
2274 /// Returns value of string, extracted from special table,
2275 /// where long strings are stored
2276 
2277 Bool_t TSQLFile::GetLongString(Long64_t objid, Int_t strid, TString &value)
2278 {
2279  if (!SQLTestTable(sqlio::StringsTable))
2280  return kFALSE;
2281 
2282  TString cmd;
2283  const char *quote = SQLIdentifierQuote();
2284  cmd.Form("SELECT %s FROM %s%s%s WHERE %s%s%s=%lld AND %s%s%s=%d", sqlio::ST_Value, quote, sqlio::StringsTable, quote,
2285  quote, SQLObjectIdColumn(), quote, objid, quote, SQLStrIdColumn(), quote, strid);
2286 
2287  TSQLResult *res = SQLQuery(cmd.Data(), 1);
2288  if (!res)
2289  return kFALSE;
2290  TSQLRow *row = res->Next();
2291  if (!row) {
2292  delete res;
2293  return kFALSE;
2294  }
2295  value = row->GetField(0);
2296 
2297  delete row;
2298  delete res;
2299 
2300  return kTRUE;
2301 }
2302 
2303 ////////////////////////////////////////////////////////////////////////////////
2304 /// Checks that objects table is exists
2305 /// If not, table will be created
2306 /// Returns maximum value for existing objects id
2307 
2308 Long64_t TSQLFile::VerifyObjectTable()
2309 {
2310  if (!fSQL)
2311  return -1;
2312 
2313  Long64_t maxid = -1;
2314 
2315  if (gDebug > 2)
2316  Info("VerifyObjectTable", "Checks if object table is there");
2317 
2318  if (SQLTestTable(sqlio::ObjectsTable))
2319  maxid = SQLMaximumValue(sqlio::ObjectsTable, SQLObjectIdColumn());
2320  else {
2321  TString sqlcmd;
2322  const char *quote = SQLIdentifierQuote();
2323  sqlcmd.Form("CREATE TABLE %s%s%s (%s%s%s %s, %s%s%s %s, %s%s%s %s, %s%s%s %s)", quote, sqlio::ObjectsTable, quote,
2324  quote, SQLKeyIdColumn(), quote, SQLIntType(), quote, SQLObjectIdColumn(), quote, SQLIntType(), quote,
2325  sqlio::OT_Class, quote, SQLSmallTextType(), quote, sqlio::OT_Version, quote, SQLIntType());
2326 
2327  if ((fTablesType.Length() > 0) && IsMySQL()) {
2328  sqlcmd += " ENGINE=";
2329  sqlcmd += fTablesType;
2330  }
2331 
2332  SQLQuery(sqlcmd.Data());
2333 
2334  if (GetUseIndexes() > kIndexesNone) {
2335  sqlcmd.Form("CREATE UNIQUE INDEX %s%s%s ON %s%s%s (%s%s%s)", quote, sqlio::ObjectsTableIndex, quote, quote,
2336  sqlio::ObjectsTable, quote, quote, SQLObjectIdColumn(), quote);
2337  SQLQuery(sqlcmd.Data());
2338  }
2339  }
2340 
2341  return maxid;
2342 }
2343 
2344 ////////////////////////////////////////////////////////////////////////////////
2345 /// Read from objects table data for specified objectid
2346 
2347 Bool_t TSQLFile::SQLObjectInfo(Long64_t objid, TString &clname, Version_t &version)
2348 {
2349  if (!fSQL)
2350  return kFALSE;
2351 
2352  TString sqlcmd;
2353  const char *quote = SQLIdentifierQuote();
2354  sqlcmd.Form("SELECT %s%s%s, %s%s%s FROM %s%s%s WHERE %s%s%s=%lld", quote, sqlio::OT_Class, quote, quote,
2355  sqlio::OT_Version, quote, quote, sqlio::ObjectsTable, quote, quote, SQLObjectIdColumn(), quote, objid);
2356  TSQLResult *res = SQLQuery(sqlcmd.Data(), 1);
2357  if (!res)
2358  return kFALSE;
2359  TSQLRow *row = res->Next();
2360  if (row) {
2361  clname = row->GetField(0);
2362  version = atoi(row->GetField(1));
2363  }
2364 
2365  delete row;
2366  delete res;
2367  return row != nullptr;
2368 }
2369 
2370 ////////////////////////////////////////////////////////////////////////////////
2371 /// Produce array of TSQLObjectInfo objects for all objects, belong to that key
2372 /// Array should be deleted by calling function afterwards
2373 
2374 TObjArray *TSQLFile::SQLObjectsInfo(Long64_t keyid)
2375 {
2376  if (!fSQL)
2377  return nullptr;
2378 
2379  TString sqlcmd;
2380  const char *quote = SQLIdentifierQuote();
2381  sqlcmd.Form("SELECT %s%s%s, %s%s%s, %s%s%s FROM %s%s%s WHERE %s%s%s=%lld ORDER BY %s%s%s", quote,
2382  SQLObjectIdColumn(), quote, quote, sqlio::OT_Class, quote, quote, sqlio::OT_Version, quote, quote,
2383  sqlio::ObjectsTable, quote, quote, SQLKeyIdColumn(), quote, keyid, quote, SQLObjectIdColumn(), quote);
2384 
2385  TObjArray *arr = nullptr;
2386 
2387  if (fLogFile)
2388  *fLogFile << sqlcmd << std::endl;
2389  if (gDebug > 2)
2390  Info("SQLObjectsInfo", "%s", sqlcmd.Data());
2391  fQuerisCounter++;
2392 
2393  TSQLStatement *stmt = SQLStatement(sqlcmd.Data(), 1000);
2394 
2395  if (stmt) {
2396  stmt->Process();
2397  stmt->StoreResult();
2398 
2399  while (stmt->NextResultRow()) {
2400  Long64_t objid = stmt->GetLong64(0);
2401  const char *clname = stmt->GetString(1);
2402  Int_t version = stmt->GetInt(2);
2403 
2404  TSQLObjectInfo *info = new TSQLObjectInfo(objid, clname, version);
2405  if (!arr)
2406  arr = new TObjArray();
2407  arr->Add(info);
2408  }
2409 
2410  delete stmt;
2411  return arr;
2412  }
2413 
2414  TSQLResult *res = SQLQuery(sqlcmd.Data(), 1);
2415  if (!res)
2416  return nullptr;
2417 
2418  TSQLRow *row = nullptr;
2419  while ((row = res->Next()) != nullptr) {
2420  Long64_t objid = atoi(row->GetField(0));
2421  const char *clname = row->GetField(1);
2422  Int_t version = atoi(row->GetField(2));
2423 
2424  TSQLObjectInfo *info = new TSQLObjectInfo(objid, clname, version);
2425  if (arr == 0)
2426  arr = new TObjArray();
2427  arr->Add(info);
2428 
2429  delete row;
2430  }
2431  delete res;
2432  return arr;
2433 }
2434 
2435 ////////////////////////////////////////////////////////////////////////////////
2436 /// Method return request result for specified objid from normal classtable
2437 
2438 TSQLResult *TSQLFile::GetNormalClassData(Long64_t objid, TSQLClassInfo *sqlinfo)
2439 {
2440  if (!sqlinfo->IsClassTableExist())
2441  return 0;
2442  TString sqlcmd;
2443  const char *quote = SQLIdentifierQuote();
2444  sqlcmd.Form("SELECT * FROM %s%s%s WHERE %s%s%s=%lld", quote, sqlinfo->GetClassTableName(), quote, quote,
2445  SQLObjectIdColumn(), quote, objid);
2446  return SQLQuery(sqlcmd.Data(), 2);
2447 }
2448 
2449 ////////////////////////////////////////////////////////////////////////////////
2450 /// Return data for several objects from the range from normal class table
2451 
2452 TSQLResult *TSQLFile::GetNormalClassDataAll(Long64_t minobjid, Long64_t maxobjid, TSQLClassInfo *sqlinfo)
2453 {
2454  if (!sqlinfo->IsClassTableExist())
2455  return nullptr;
2456  TString sqlcmd;
2457  const char *quote = SQLIdentifierQuote();
2458  sqlcmd.Form("SELECT * FROM %s%s%s WHERE %s%s%s BETWEEN %lld AND %lld ORDER BY %s%s%s", quote,
2459  sqlinfo->GetClassTableName(), quote, quote, SQLObjectIdColumn(), quote, minobjid, maxobjid, quote,
2460  SQLObjectIdColumn(), quote);
2461  return SQLQuery(sqlcmd.Data(), 2);
2462 }
2463 
2464 ////////////////////////////////////////////////////////////////////////////////
2465 /// Method return request results for specified objid from _streamer_ classtable
2466 
2467 TSQLResult *TSQLFile::GetBlobClassData(Long64_t objid, TSQLClassInfo *sqlinfo)
2468 {
2469  if (!sqlinfo->IsRawTableExist())
2470  return nullptr;
2471  TString sqlcmd;
2472  const char *quote = SQLIdentifierQuote();
2473  sqlcmd.Form("SELECT %s, %s FROM %s%s%s WHERE %s%s%s=%lld ORDER BY %s%s%s", sqlio::BT_Field, sqlio::BT_Value, quote,
2474  sqlinfo->GetRawTableName(), quote, quote, SQLObjectIdColumn(), quote, objid, quote, SQLRawIdColumn(),
2475  quote);
2476  return SQLQuery(sqlcmd.Data(), 2);
2477 }
2478 
2479 ////////////////////////////////////////////////////////////////////////////////
2480 /// Method return request results for specified objid from _streamer_ classtable
2481 /// Data returned in form of statement, where direct access to values are possible
2482 
2483 TSQLStatement *TSQLFile::GetBlobClassDataStmt(Long64_t objid, TSQLClassInfo *sqlinfo)
2484 {
2485  if (!sqlinfo->IsRawTableExist())
2486  return nullptr;
2487 
2488  TString sqlcmd;
2489  const char *quote = SQLIdentifierQuote();
2490  sqlcmd.Form("SELECT %s, %s FROM %s%s%s WHERE %s%s%s=%lld ORDER BY %s%s%s", sqlio::BT_Field, sqlio::BT_Value, quote,
2491  sqlinfo->GetRawTableName(), quote, quote, SQLObjectIdColumn(), quote, objid, quote, SQLRawIdColumn(),
2492  quote);
2493 
2494  if (fLogFile)
2495  *fLogFile << sqlcmd << std::endl;
2496  if (gDebug > 2)
2497  Info("BuildStatement", "%s", sqlcmd.Data());
2498  fQuerisCounter++;
2499 
2500  TSQLStatement *stmt = SQLStatement(sqlcmd.Data(), 1000);
2501  if (!stmt)
2502  return nullptr;
2503 
2504  stmt->Process();
2505 
2506  stmt->StoreResult();
2507 
2508  return stmt;
2509 }
2510 
2511 ////////////////////////////////////////////////////////////////////////////////
2512 /// Store object in database. Return stored object id or -1 if error
2513 
2514 Long64_t TSQLFile::StoreObjectInTables(Long64_t keyid, const void *obj, const TClass *cl)
2515 {
2516  if (!fSQL)
2517  return -1;
2518 
2519  Long64_t objid = VerifyObjectTable();
2520  if (objid <= 0)
2521  objid = 1;
2522  else
2523  objid++;
2524 
2525  TBufferSQL2 buffer(TBuffer::kWrite, this);
2526 
2527  buffer.InitMap();
2528 
2529  TSQLStructure *s = buffer.SqlWriteAny(obj, cl, objid);
2530 
2531  if ((buffer.GetErrorFlag() > 0) && s) {
2532  Error("StoreObjectInTables", "Cannot convert object data to TSQLStructure");
2533  objid = -1;
2534  } else {
2535  TObjArray cmds;
2536  // here tables may be already created, therefore
2537  // it should be protected by transactions operations
2538  if (s && !s->ConvertToTables(this, keyid, &cmds)) {
2539  Error("StoreObjectInTables", "Cannot convert to SQL statements");
2540  objid = -1;
2541  } else {
2542  Bool_t needcommit = kFALSE;
2543 
2544  if (GetUseTransactions() == kTransactionsAuto) {
2545  SQLStartTransaction();
2546  needcommit = kTRUE;
2547  }
2548 
2549  if (!SQLApplyCommands(&cmds)) {
2550  Error("StoreObject", "Cannot correctly store object data in database");
2551  objid = -1;
2552  if (needcommit)
2553  SQLRollback();
2554  } else {
2555  if (needcommit)
2556  SQLCommit();
2557  }
2558  }
2559  cmds.Delete();
2560  }
2561 
2562  return objid;
2563 }
2564 
2565 ////////////////////////////////////////////////////////////////////////////////
2566 /// Returns sql type name which is most closer to ROOT basic type.
2567 /// typ should be from TVirtualStreamerInfo:: constansts like TVirtualStreamerInfo::kInt
2568 
2569 const char *TSQLFile::SQLCompatibleType(Int_t typ) const
2570 {
2571  return (typ < 0) || (typ > 18) ? 0 : fBasicTypes[typ];
2572 }
2573 
2574 ////////////////////////////////////////////////////////////////////////////////
2575 /// return SQL integer type
2576 
2577 const char *TSQLFile::SQLIntType() const
2578 {
2579  return SQLCompatibleType(TVirtualStreamerInfo::kInt);
2580 }
2581 
2582 ////////////////////////////////////////////////////////////////////////////////
2583 /// Create entry for directory in database
2584 
2585 Long64_t TSQLFile::DirCreateEntry(TDirectory *dir)
2586 {
2587  TDirectory *mother = dir->GetMotherDir();
2588  if (!mother)
2589  mother = this;
2590 
2591  // key will be added to mother directory
2592  TKeySQL *key = new TKeySQL(mother, dir, dir->GetName(), dir->GetTitle());
2593 
2594  return key->GetDBKeyId();
2595 }
2596 
2597 ////////////////////////////////////////////////////////////////////////////////
2598 /// Read directory list of keys from database
2599 
2600 Int_t TSQLFile::DirReadKeys(TDirectory *dir)
2601 {
2602  // First delete all old keys
2603  dir->GetListOfKeys()->Delete();
2604 
2605  if (gDebug > 2)
2606  Info("DirReadKeys", "dir = %s id = %lld", dir->GetName(), dir->GetSeekDir());
2607 
2608  return StreamKeysForDirectory(dir, kFALSE);
2609 }
2610 
2611 ////////////////////////////////////////////////////////////////////////////////
2612 /// Write directory keys list to database
2613 
2614 void TSQLFile::DirWriteKeys(TDirectory *dir)
2615 {
2616  StreamKeysForDirectory(dir, kTRUE);
2617 }
2618 
2619 ////////////////////////////////////////////////////////////////////////////////
2620 /// Update dir header in the file
2621 
2622 void TSQLFile::DirWriteHeader(TDirectory *dir)
2623 {
2624  TSQLClassInfo *sqlinfo = FindSQLClassInfo("TDirectory", TDirectoryFile::Class()->GetClassVersion());
2625  if (!sqlinfo)
2626  return;
2627 
2628  // try to identify key with data for our directory
2629  TKeySQL *key = FindSQLKey(dir->GetMotherDir(), dir->GetSeekDir());
2630  if (!key)
2631  return;
2632 
2633  const char *valuequote = SQLValueQuote();
2634  const char *quote = SQLIdentifierQuote();
2635 
2636  TDirectoryFile *fdir = dynamic_cast<TDirectoryFile *> (dir);
2637  TString timeC = fdir ? fdir->GetCreationDate().AsSQLString() : fDatimeC.AsSQLString();
2638  TString timeM = fdir ? fdir->GetModificationDate().AsSQLString() : fDatimeM.AsSQLString();;
2639  if (TestBit(TFile::kReproducible))
2640  timeC = timeM = TDatime((UInt_t) 1).AsSQLString();
2641 
2642  TSQLStructure::AddStrBrackets(timeC, valuequote);
2643  TSQLStructure::AddStrBrackets(timeM, valuequote);
2644 
2645  TString uuid = TestBit(TFile::kReproducible) ? TUUID("00000000-0000-0000-0000-000000000000").AsString() : dir->GetUUID().AsString();
2646  TSQLStructure::AddStrBrackets(uuid, valuequote);
2647 
2648  TString sqlcmd;
2649 
2650  TString col1name = "CreateTime";
2651  TString col2name = "ModifyTime";
2652  TString col3name = "UUID";
2653  if (GetUseSuffixes()) {
2654  col1name += sqlio::StrSuffix;
2655  col2name += sqlio::StrSuffix;
2656  col3name += sqlio::StrSuffix;
2657  }
2658 
2659  sqlcmd.Form("UPDATE %s%s%s SET %s%s%s=%s, %s%s%s=%s, %s%s%s=%s WHERE %s%s%s=%lld", quote,
2660  sqlinfo->GetClassTableName(), quote, quote, col1name.Data(), quote, timeC.Data(), quote, col2name.Data(),
2661  quote, timeM.Data(), quote, col3name.Data(), quote, uuid.Data(), quote, SQLObjectIdColumn(), quote,
2662  key->GetDBObjId());
2663 
2664  SQLQuery(sqlcmd.Data());
2665 }
2666 
2667 ////////////////////////////////////////////////////////////////////////////////
2668 /// Streamer for TSQLFile class.
2669 /// Stores only data for TDirectory.
2670 
2671 void TSQLFile::Streamer(TBuffer &b)
2672 {
2673  TString sbuf;
2674 
2675  if (b.IsReading()) {
2676  Version_t R__v = b.ReadVersion(0, 0);
2677  b.ClassBegin(TSQLFile::Class(), R__v);
2678 
2679  b.ClassMember("CreateTime", "TString");
2680  sbuf.Streamer(b);
2681  TDatime timeC(sbuf.Data());
2682  fDatimeC = timeC;
2683 
2684  b.ClassMember("ModifyTime", "TString");
2685  sbuf.Streamer(b);
2686  TDatime timeM(sbuf.Data());
2687  fDatimeM = timeM;
2688 
2689  b.ClassMember("UUID", "TString");
2690  sbuf.Streamer(b);
2691  TUUID id(sbuf.Data());
2692  fUUID = id;
2693 
2694  b.ClassEnd(TSQLFile::Class());
2695  } else {
2696 
2697  b.WriteVersion(TSQLFile::Class());
2698 
2699  b.ClassBegin(TSQLFile::Class());
2700 
2701  b.ClassMember("CreateTime", "TString");
2702  sbuf = TestBit(TFile::kReproducible) ? TDatime((UInt_t) 1).AsSQLString() : fDatimeC.AsSQLString();
2703 
2704  sbuf.Streamer(b);
2705 
2706  b.ClassMember("ModifyTime", "TString");
2707  fDatimeM.Set();
2708  sbuf = TestBit(TFile::kReproducible) ? TDatime((UInt_t) 1).AsSQLString() : fDatimeM.AsSQLString();
2709  sbuf.Streamer(b);
2710 
2711  b.ClassMember("UUID", "TString");
2712  sbuf = TestBit(TFile::kReproducible) ? TUUID("00000000-0000-0000-0000-000000000000").AsString() : fUUID.AsString();
2713  sbuf.Streamer(b);
2714 
2715  b.ClassEnd(TSQLFile::Class());
2716  }
2717 }