KaliVeda  1.13/01
Heavy-Ion Analysis Toolkit
SQLiteDB.cpp
Go to the documentation of this file.
1 //Created by KVClassFactory on Mon Jul 25 15:33:56 2016
2 //Author: John Frankland,,,
3 
4 #include "SQLiteDB.h"
5 #include <TList.h>
6 #include "TSQLResult.h"
7 #include "TSQLRow.h"
8 #include "TSQLTableInfo.h"
9 #include "TSQLColumnInfo.h"
10 #include <KVString.h>
11 #include <iostream>
12 #include "KVError.h"
13 #include <iomanip>
14 #include "TSystem.h"
15 
19 
20 
21 namespace KVSQLite {
22 
23  // static maps instantiation
24  std::map<TString, KVSQLite::column_type::types> table::type_map;
25  std::map<KVSQLite::column_type::types, TString> column::inv_type_map;
26 
27 
33 
34  std::unique_ptr<TSQLResult> database::SelectRowsFromTable(const TString& table, const TString& columns, const TString& condition) const
35  {
36  // \param[in] table name of table
37  // \param[in] columns comma-separated list of columns
38  // \param[in] condition selection to be applied, if any
39  // \returns result of query `SELECT [columns] FROM [table] WHERE [condition]`
40 
41  TString query;
42  query.Form("SELECT %s FROM '%s'", columns.Data(), table.Data());
43  if (condition != "") query += Form(" WHERE %s", condition.Data());
44  return std::unique_ptr<TSQLResult>(fDBserv->Query(query));
45  }
46 
47 
48 
51 
53  {
54  // initialise map of database tables from existing database
55  std::unique_ptr<TList> tl(fDBserv->GetTablesList());
56  TObject* o;
57  TIter it_tab(tl.get());
58  while ((o = it_tab())) {
59 
60  table t(o->GetName());
61 
62  std::unique_ptr<TSQLResult> columnRes(fDBserv->GetColumns("", o->GetName()));
63  if (!columnRes.get()) {
64  Error("read_table_infos", "Cannot get information on columns for table %s", o->GetName());
65  return;
66  }
67  std::unique_ptr<TSQLRow> columnRow(columnRes->Next());
68  while ((columnRow.get()) != nullptr) {
69  column& col = t.add_column(columnRow->GetField(1), columnRow->GetField(2));
70  TString primary_key(columnRow->GetField(5));
71  if (primary_key.Atoi() == 1) col.set_constraint("PRIMARY KEY");
72  columnRow.reset(columnRes->Next());
73  }
74  fTables.insert(std::pair<std::string, KVSQLite::table>(o->GetName(), t));
75  }
76 
77  }
78 
79 
80 
83 
84  void database::show_tables() const
85  {
86  // print list of tables
87  std::cout << "Tables in database:" << std::endl;
88 #ifdef WITH_CPP11
89  for (auto it = fTables.begin();
90 #else
91  for (std::map<std::string, KVSQLite::table>::const_iterator it = fTables.begin();
92 #endif
93  it != fTables.end(); ++it) {
94  std::cout << "\t" << it->first << std::endl;
95  }
96  }
97 
98 
99 
109 
110  void database::open(const TString& dbfile)
111  {
112  // Open/create sqlite db file given path
113  //
114  // Any special characters/environment variables are first expanded, so
115  // you can use:
116  //~~~~
117  // ~/mydata.db
118  // $(SOME_PATH)/toto.db
119  //~~~~
120 
121  TString exp_path = dbfile;
122  if (gSystem->ExpandPathName(exp_path)) {
123  Error("open", "problem with SQLite database filename: %s", dbfile.Data());
124  fIsValid = false;
125  return;
126  }
127  TString uri = "sqlite://" + exp_path;
128  fDBserv.reset(static_cast<TSQLiteServer*>(TSQLServer::Connect(uri, 0, 0)));
129  // check for valid database file
130  if (!fDBserv->Exec("pragma schema_version")) {
131  fDBserv->Close();
132  fIsValid = false;
133  return;
134  }
135  fIsValid = true;
137  }
138 
139 
140 
142 
143  void database::PrintResults(TSQLResult* tabent, int column_width) const
144  {
145  int nfields = tabent->GetFieldCount();
146  std::unique_ptr<TSQLRow> row(nullptr);
147  for (int r = -1; true; ++r) {
148  if (r > -1) {
149  row.reset(tabent->Next());
150  if (row.get() == nullptr) break;
151  std::cout << std::setw(6) << r;
152  }
153  for (int f = 0; f < nfields; ++f) {
154  if (r < 0) {
155  if (f == 0) std::cout << std::setw(6) << "#";
156  std::cout << "|" << std::setw(column_width) << tabent->GetFieldName(f) ;
157  }
158  else {
159  std::cout << "|" << std::setw(column_width) << row->GetField(f) ;
160  }
161  }
162  std::cout << "\n";
163  }
164  }
165 
166 
167 
170 
171  void database::Dump() const
172  {
173  // Print on stdout contents of database
174 
175  std::cout << "Database : " << fDBserv->GetDB() << " [" << fDBserv->GetDBMS() << "]\n";
176  std::unique_ptr<TList> tl(fDBserv->GetTablesList());
177  TObject* o;
178  TIter it_tab(tl.get());
179  while ((o = it_tab())) {
180  std::cout << "\t";
181  std::cout << "Table : " << o->GetName() << "\n";
182  std::unique_ptr<TSQLResult> tabent = SelectRowsFromTable(o->GetName());
183  PrintResults(tabent.get());
184  }
185  std::cout << std::endl;
186  }
187 
188 
191 
192  void database::print_selection(const TString& table, const TString& columns, const TString& condition, int column_width) const
193  {
194  // Print on stdout contents of database
195 
196  std::unique_ptr<TSQLResult> tabent = SelectRowsFromTable(table, columns, condition);
197  PrintResults(tabent.get(), column_width);
198  std::cout << std::endl;
199  }
200 
201 
202 
218 
219  void database::add_table(const table& t)
220  {
221  // add table to database (if it does not exist already)
222  //
223  // _WARNING:_ after calling this method, do not use the table given as argument
224  //
225  // it does not correspond to the table in the database
226  //
227  // instead use `db["table name"]` to access the table
228  //
229  //~~~~{.cpp}
230  // e.g. KVSQLite::table tt("some table");
231  // tt.add_column(...);
232  // db.add_table(tt);
233  // db["some table"]["column"].set_data(...);
234  //~~~~
235 
236  TString command("CREATE ");
237  if (t.is_temporary()) command += "TEMPORARY ";
238  command += "TABLE IF NOT EXISTS \"";
239  command += t.name();
240  command += "\"";
241  command += " (";
242  for (int i = 0; i < t.number_of_columns(); ++i) {
243  if (i) command += ", ";
244  command += t[i].get_declaration();
245  }
246  command += ")";
247  //std::cout << command << std::endl;
248  if (fDBserv->Exec(command))
249  fTables.insert(std::pair<std::string, KVSQLite::table>(t.name(), t));
250  }
251 
252 
253 
268 
270  {
271  // Call this method before insert_dat_row() in order to perform bulk data
272  // insertion operation. i.e. something like:
273  //
274  //~~~~{.cpp}
275  // db.prepare_data_insertion("my_table");
276  // while(...){ // loop over data to insert
277  // // set up data in table
278  // db.insert_data_row();
279  // }
280  // db.end_data_insertion(); // terminate data insertion
281  //~~~~
282  // Until method end_data_insertion() is called, you cannot call prepare_data_insertion()
283  // with a different table name.
284 
285  if (fInserting) {
286  if (fBulkTable) {
287  Error("database::prepare_data_insertion",
288  "bulk insertion in progress for table %s; call database::commit() to terminate transaction",
289  fBulkTable->name());
290  return false;
291  }
292  else {
293  Error("database::prepare_data_insertion",
294  "bulk insertion in progress; call database::commit() to terminate transaction");
295  return false;
296  }
297  }
298  if (fSelecting) {
299  Error("database::prepare_data_insertion",
300  "data retrieval in progress; call get_next_result() until it returns false");
301  return false;
302  }
303  fInserting = true;
304  fDBserv->StartTransaction();
305  // set up SQL statement for data insertion into table
306  fBulkTable = &fTables[table.Data()];
308  int ncol = fBulkTable->number_of_columns();
309  int idx = 0;
310  for (int i = 0; i < ncol; ++i) {
311  if (idx) com += ",";
312  if (!(*fBulkTable)[i].primary_key()) {
313  com += Form("\"%s\"", (*fBulkTable)[i].name());;
314  ++idx;
315  }
316  }
317  com += ") VALUES (";
318  idx = 0;
319  for (int i = 0; i < ncol; ++i) {
320  if (idx) com += ",";
321  if (!(*fBulkTable)[i].primary_key()) {
322  com += "?";
323  ++idx;
324  }
325  }
326  com += ")";
327  //std::cout << com << std::endl;
328  fSQLstmt.reset(fDBserv->Statement(com));
329  return true;
330  }
331 
332 
333 
335 
336  const char* table::get_insert_command() const
337  {
338  switch (fInsert) {
340  return Form("INSERT OR FAIL INTO \"%s\"(", name());
341  break;
343  return Form("INSERT OR IGNORE INTO \"%s\"(", name());
344  break;
346  return Form("INSERT OR REPLACE INTO \"%s\"(", name());
347  break;
348  default:
350  break;
351  }
352  return Form("INSERT INTO \"%s\"(", name());
353  }
354 
355 
356 
359 
360  const char* column::get_declaration() const
361  {
362  // return declaration for column, including type & constraint
363 
364  static TString decl;
365  decl.Form("\"%s\" %s", name(), type_name());
366  if (fForeignKey) {
367  decl += ", FOREIGN KEY";
368  decl += Form("(\"%s\")", name());
369  decl += " REFERENCES ";
370  decl += Form("\"%s\"(\"%s\")", fFKtable.Data(), fFKcolumn.Data());
371  }
372  else {
373  decl += " ";
374  decl += fConstraint;
375  }
376  return decl.Data();
377  }
378 
379 
380 
392 
394  {
395  // Call (repeatedly) after a call to prepare_data_insertion()
396  // in order to insert current contents of table columns as a new row in the database.
397  //
398  // Value of each column should first be set like this:
399  //
400  //~~~~{.cpp}
401  // db["table"]["id"].set_data(6);
402  // db["table"]["name"].set_data("triumph");
403  //~~~~
404  // Call end_data_insertion() when all data has been inserted
405 
406  if (!fInserting || !fBulkTable) {
407  Error("database::insert_data_row",
408  "no transaction initialized; call prepare_data_insertion(name_table) before this method");
409  return;
410  }
411  if (fSelecting) {
412  Error("database::insert_data_row",
413  "data retrieval in progress; call get_next_result() until it returns false, then call prepare_data_insertion(name_table) before this method");
414  return;
415  }
416  int ncol = fBulkTable->number_of_columns();
417  fSQLstmt->NextIteration();
418  int idx = 0;
419  for (int i = 0; i < ncol; ++i) {
420  if (!(*fBulkTable)[i].primary_key()) {
421  (*fBulkTable)[i].set_data_in_statement(fSQLstmt.get(), idx);
422  ++idx;
423  }
424  }
425  }
426 
427 
428 
432 
434  {
435  // Call after prepare_data_insertion() & insert_data_row() have been
436  // used to insert data into a table
437 
438  if (!fInserting) {
439  Error("database::end_data_insertion",
440  "no transaction initialized; call prepare_data_insertion(name_table) first");
441  return;
442  }
443  if (fSelecting) {
444  Error("database::insert_data_row",
445  "data retrieval in progress; call get_next_result() until it returns false, then call prepare_data_insertion(name_table) before this method");
446  return;
447  }
448  fBulkTable = nullptr;
449  fSQLstmt->Process();
450  fDBserv->Commit();
451  fInserting = false;
452  }
453 
454 
457 
458  void database::print_selected_data(const TString& tables, const TString& columns, const TString& selection, bool distinct, const TString& anything_else)
459  {
460  // Print out results of a call to select_data().
461 
462  if (select_data(tables, columns, selection, distinct, anything_else)) {
463  for (auto col : fSQLstmtCols) {
464  std::cout << col->get_table() << "::" << col->name() << "\t\t\t";
465  }
466  std::cout << std::endl;
467  while (get_next_result()) {
468  for (auto col : fSQLstmtCols) {
469  std::cout << col->data().GetString() << "\t\t\t";
470  }
471  std::cout << std::endl;
472  }
473  }
474  }
475 
476 
492 
493  bool database::select_data(const TString& tables, const TString& columns, const TString& selection, bool distinct, const TString& anything_else) const
494  {
495  // Select data in database from given table(s) according to
496  //~~~~
497  // SELECT [columns] FROM [tables] WHERE [selection] [anything_else]
498  //~~~~
499  // In order to retrieve results, call get_next_result() until it returns false.
500  //
501  // \param tables if more than 1 table is given, separate table names with commas.
502  // if 1 table has a foreign key referencing the other, this allows to JOIN data
503  // in both tables together. [columns] can then refer to columns in either table.
504  // \param columns ="*" by default, i.e. data from all columns is retrieved.
505  // If specific column data is to be selected, give a comma-separated list of
506  // column names. These will be quoted correctly in case they contain spaces.
507  // \param distinct can be used in conjunction with a selection of specific columns in order
508  // to retrieve only rows of data with different values for the column(s).
509  if (fInserting) {
510  Error("database::select_data",
511  "data insertion in progress; call end_data_insertion() before retrieving data");
512  return false;
513  }
514  if (fSelecting) {
515  Error("database::select_data",
516  "data retrieval already in progress; call get_next_result() until it returns false before making new selection");
517  return false;
518  }
519  fSQLstmtCols.clear();
520  std::list<const table*> table_list;
521  KVString table_selection(""), _tables(tables);
522  _tables.Begin(",");
523  // put quoted table names in table_selection
524  int i(0);
525  while (!_tables.End()) {
526  TString tabnam = _tables.Next();
527  table_list.push_back(& operator[](tabnam));
528  if (i) table_selection += " NATURAL JOIN ";
529  table_selection += Form("\"%s\"", tabnam.Data());
530  ++i;
531  }
532 
533  KVString column_selection(""), _columns(columns);
534  if (columns == "*") {
535  column_selection = "*";
536  distinct = false; // don't allow 'SELECT DISTINCT * FROM ....' (?)
537  // find right column in right table for each item
538  // loop over all columns in each table
539  for (auto tabs : table_list) {
540  for (int i = 0; i < tabs->number_of_columns(); ++i) {
541  fSQLstmtCols.push_back(&(*tabs)[i]);
542  }
543  }
544  }
545  else {
546  if (distinct) column_selection = "DISTINCT ";
547  // put quoted column names in column_selection
548  _columns.Begin(",");
549  int i(0);
550  while (!_columns.End()) {
551  TString colnam = _columns.Next();
552  if (i) column_selection += ", ";
553  column_selection += Form("\"%s\"", colnam.Data());
554  ++i;
555  // find right column in right table for this item
556  for (auto tabs : table_list) if (tabs->has_column(colnam)) fSQLstmtCols.push_back(&(*tabs)[colnam]);
557  }
558  }
559 
560  TString cond = Form("SELECT %s FROM %s", column_selection.Data(), table_selection.Data());
561  if (selection != "") cond += Form(" WHERE %s", selection.Data());
562  if (anything_else != "") cond += Form(" %s", anything_else.Data());
563  fSQLstmt.reset(fDBserv->Statement(cond));
564  if (fSQLstmt.get() == nullptr) {
565  Error("database::select_data", "problem processing : %s", cond.Data());
566  fSelecting = false;
567  fBulkTable = nullptr;
568  return false;
569  }
570  fSQLstmt->EnableErrorOutput();
571  if (fSQLstmt->Process()) {
572  fSQLstmt->StoreResult();
573  fSelecting = true;
574  fEmptyResultSet = false;
575  return true;
576  }
577  else if (!fSQLstmt->IsError()) {
578  // query ok, no results correspond to selection
579  fSQLstmt->StoreResult();
580  fSelecting = true;
581  fEmptyResultSet = true;
582  return true;
583  }
584  fSelecting = false;
585  fBulkTable = nullptr;
586  return false;
587  }
588 
589 
590 
594 
596  {
597  // Retrieve next result row resulting from previous call to select_data()
598  // \returns kFALSE when no more data is retrieved
599 
600  if (fInserting) {
601  Error("database::get_next_result",
602  "data insertion in progress; call end_data_insertion() then select_data() before this method");
603  return false;
604  }
605  if (!fSelecting) {
606  Error("database::get_next_result",
607  "no data retrieval in progress; select_data() must be called and return true before calling this method");
608  return false;
609  }
610  if (!fEmptyResultSet && fSQLstmt->NextResultRow()) {
611  // set column data
612  int idx = 0;
613  for (auto col : fSQLstmtCols) {
614  col->set_data_from_statement(fSQLstmt.get(), idx);
615  ++idx;
616  }
617  return kTRUE;
618  }
619  fBulkTable = nullptr;
620  fSelecting = false;
621  return kFALSE;
622  }
623 
624 
625 
629 
630  KVNumberList database::get_integer_list(const TString& table, const TString& column, const TString& selection, const TString& anything_else)
631  {
632  // \note Only for INTEGER columns!
633  // Fill KVNumberList with all `DISTINCT` values of "column" (only 1 column name at a time) for given selection
634 
635  KVNumberList result;
636  if (select_data(table, column, selection, true, anything_else)) {
637  while (get_next_result()) {
638  result.Add((*this)[table][column].get_data<int>());
639  }
640  }
641  return result;
642  }
643 
644 
645 
650 
651  TString database::get_string_list(const TString& table, const TString& column, const TString& selection, const TString& anything_else)
652  {
653  // \note Only for TEXT columns!
654  // Fill TString with comma-separated list of values of "column" (only 1 column name at a time) for given selection
655  // \note Any NULL entries will be ignored
656 
657  TString result;
658  if (select_data(table, column, selection, false, anything_else)) {
659  while (get_next_result()) {
660  if ((*this)[table][column].is_null()) continue;
661  if (result != "") result += ",";
662  result += (*this)[table][column].get_data<TString>();
663  }
664  }
665  return result;
666  }
667 
668 
669 
674 
675  KVNameValueList database::get_name_value_list(const TString& tablename, const TString& name_column, const TString& value_column, const TString& selection, const TString& anything_else)
676  {
677  // Fill KVNameValueList with selected rows from table, adding for each row a parameter with the
678  // name contained in "name_column" (must be of type `TEXT`) and the value contained in "value_column"
679  // (can be `INTEGER`, `REAL`, or `TEXT`)
680 
681  KVNameValueList result;
682  if (select_data(tablename, Form("%s,%s", name_column.Data(), value_column.Data()), selection, false, anything_else)) {
683  table& tb = (*this)[tablename];
684  column& nom = tb[name_column];
685  column& val = tb[value_column];
686  while (get_next_result()) {
687  result.SetValue(nom.get_data<TString>(), val.data());
688  }
689  }
690  return result;
691  }
692 
693 
694 
698 
699  TGraph* database::create_graph(const TString& tablename, const TString& Xcolumn, const TString& Ycolumn, const TString& selection)
700  {
701  // Create and fill a TGraph from values Xcolumn and Ycolumn in table,
702  // using the selection if required
703 
704  if (select_data(tablename, Form("%s,%s", Xcolumn.Data(), Ycolumn.Data()), selection)) {
705  TGraph* g = new TGraph;
706  int i = 0;
707  table& tb = (*this)[tablename];
708  column& Xcol = tb[Xcolumn];
709  column& Ycol = tb[Ycolumn];
710  while (get_next_result()) {
711  g->SetPoint(i++, Xcol.get_data<double>(), Ycol.get_data<double>());
712  }
713  return g;
714  }
715  return nullptr;
716  }
717 
718 
719 
722 
723  void database::clear_table(const TString& name)
724  {
725  // Delete all data from table
726  delete_data(name);
727  }
728 
729 
730 
738 
739  int database::count(const TString& table, const TString& column, const TString& selection, bool distinct) const
740  {
741  // Returns number of rows in table for which selection holds true:
742  //
743  // + if column="*" all rows are included
744  // + if a column name is given, only rows with a non-NULL value for column are counted
745  // + if distinct=false, count all rows including those with the same value of column
746  // + if distinct=true, count the number of different values of column
747 
748  TString qry = "SELECT count(";
749  if (distinct) qry += "DISTINCT ";
750  qry += Form("\"%s\"", column.Data());
751  qry += ") FROM '";
752  qry += table;
753  qry += "'";
754  if (selection != "") {
755  qry += " WHERE ";
756  qry += selection;
757  }
758 
759  std::unique_ptr<TSQLResult> result(fDBserv->Query(qry));
760  std::unique_ptr<TSQLRow> row(result->Next());
761  TString number = row->GetField(0);
762  return number.Atoi();
763  }
764 
765 
766 
775 
776  bool database::update(const TString& table, const TString& columns, const TString& selection)
777  {
778  // update the given columns of an entry in the table corresponding to selection (if given)
779  // the current values of the data members of the columns will be used
780  //
781  // This is equivalent to
782  //~~~~
783  // UPDATE [table] SET col1=newval,col2=newval,... [WHERE [selection]]
784  //~~~~
785 
786  if (fInserting) {
787  Error("database::update",
788  "data insertion in progress; call end_data_insertion() before doing anything else");
789  return false;
790  }
791  if (fSelecting) {
792  Error("database::update",
793  "data retrieval in progress; call get_next_result() until it returns false before doing anything else");
794  return false;
795  }
796 
797  fBulkTable = &fTables[table.Data()];
798  TString query = Form("UPDATE \"%s\" SET ", table.Data());
799  int ncol = fBulkTable->number_of_columns();
800  int idx = 0;
801  for (int i = 0; i < ncol; ++i) {
802  if (columns.Contains((*fBulkTable)[i].name())) {
803  if (idx) query += ",";
804  query += Form("\"%s\"", (*fBulkTable)[i].name());
805  query += "=?";
806  ++idx;
807  }
808  }
809  if (selection != "") query += Form(" WHERE %s", selection.Data());
810  //std::cout << query << std::endl;
811  fSQLstmt.reset(fDBserv->Statement(query));
812  fSQLstmt->NextIteration();
813  idx = 0;
814  for (int i = 0; i < ncol; ++i) {
815  if (columns.Contains((*fBulkTable)[i].name())) {
816  (*fBulkTable)[i].set_data_in_statement(fSQLstmt.get(), idx);
817  ++idx;
818  }
819  }
820  return (fSQLstmt->Process());
821  }
822 
823 
824 
833 
834  void database::delete_data(const TString& table, const TString& selection)
835  {
836  // delete rows from the table corresponding to selection
837  //
838  // This is equivalent to
839  //~~~~
840  // DELETE FROM [table] WHERE [selection]
841  //~~~~
842  // With no selection, deletes all rows of table (clear_table())
843 
844  TString query = Form("DELETE FROM \"%s\"", table.Data());
845  if (selection != "") query += Form(" WHERE %s", selection.Data());
846  fDBserv->Exec(query);
847  }
848 
849 
850 
854 
855  column& database::add_column(const TString& table, const TString& name, const TString& type)
856  {
857  // add column to existing table
858  // \returns reference to new column
859  TString query = Form("ALTER TABLE \"%s\" ADD COLUMN \"%s\" %s", table.Data(), name.Data(), type.Data());
860  fDBserv->Exec(query);
861  return (*this)[table].add_column(name, type);
862  }
863 
864 
865 
869 
871  {
872  // add to table any columns which are defined in the list but don't exist
873  // \note cannot be called during data insertion or retrieval!!!
874 
875  if (fInserting) {
876  Error("database::add_missing_columns",
877  "data insertion in progress; call end_data_insertion() before doing anything else");
878  return;
879  }
880  if (fSelecting) {
881  Error("database::add_missing_columns",
882  "data retrieval in progress; call get_next_result() until it returns false before doing anything else");
883  return;
884  }
885  int ipar = l.GetNpar();
886  table& tab = (*this)[_table_];
887  for (int i = 0; i < ipar; ++i) {
888  KVNamedParameter* par = l.GetParameter(i);
889  if (!tab.has_column(par->GetName())) add_column(_table_, par->GetName(), par->GetSQLType());
890  }
891  }
892 
893 
894 
902 
903  void database::copy_table_data(const TString& source, const TString& destination, const TString& columns, const TString& selection)
904  {
905  // Copy all selected data in 'source' table to 'destination'
906  //
907  // If the columns of the two tables are not identical, specify the columns to copy in 'columns'
908  // (comma-separated list)
909  // \note SQLite will not allow copy if the number of selected columns from 'source' is not
910  // exactly equal to the number of columns in 'destination'
911 
912  TString COLUMNS = columns;
913  if (COLUMNS != "*") {
914  // quote all column names
915  COLUMNS = "";
916  KVString _columns(columns);
917  _columns.Begin(",");
918  while (!_columns.End()) {
919  if (COLUMNS != "") COLUMNS += ", ";
920  COLUMNS += Form("\"%s\"", _columns.Next(kTRUE).Data());
921  }
922  }
923  TString query = Form("INSERT INTO \"%s\" SELECT %s FROM %s", destination.Data(), COLUMNS.Data(), source.Data());
924  if (selection != "") query += Form(" WHERE %s", selection.Data());
925  fDBserv->Exec(query);
926  }
927 
928 
929 
931 
933  {
938  }
939 
940 
941 
943 
944  const char* column::_type()
945  {
946  return inv_type_map[fNameType.second];
947  }
948 
949 
950 
952 
953  template<> void column::set_data(const KVNamedParameter& x)
954  {
955  fData.Set(x.GetName(), x);
956  fIsNull = false;
957  }
958 
959 
960 
966 
968  {
969  // set value of parameter in SQLite statement corresponding to this column
970  // \param idx if given, use it as the statement parameter index instead of
971  // the column's index in the table (case where not all columns are treated
972  // in the statement)
973 
974  if (idx < 0) idx = index();
975  if (fIsNull) {
976  // null parameter
977  s->SetNull(idx);
978  return;
979  }
980  switch (type()) {
982  s->SetDouble(idx, fData.GetDouble());
983  break;
985  s->SetInt(idx, fData.GetInt());
986  break;
988  s->SetString(idx, fData.GetString(), -1);
989  break;
991  s->SetBinary(idx, fBlob, fBlobSize);
992  break;
993  default:
994  break;
995  }
996  }
997 
998 
1009 
1011  {
1012  // set value of column according to value of parameter in statement
1013  //
1014  // any column which has a NULL value will be given value 0, 0.0 or ""
1015  // (for `INTEGER`, `REAL` or `TEXT` type, respectively): use column::is_null()
1016  // to check if this corresponds to a null column value.
1017  //
1018  // \param idx if given, use it as the statement parameter index instead of
1019  // the column's index in the table (case where not all columns are treated
1020  // in the statement)
1021 
1022  if (idx < 0) idx = index();
1023  fIsNull = s->IsNull(idx);
1024  switch (type()) {
1026  fData.Set(fIsNull ? 0.0 : s->GetDouble(idx));
1027  break;
1029  fData.Set(fIsNull ? 0 : s->GetInt(idx));
1030  break;
1032  fData.Set(fIsNull ? "" : s->GetString(idx));
1033  break;
1035  if (fIsNull) {
1036  fBlobSize = 0;
1037  }
1038  else {
1039  if (!fBlob) fBlob = (void*) new unsigned char[256];
1040  s->GetBinary(idx, fBlob, fBlobSize);
1041  }
1042  break;
1043  default:
1044  break;
1045  }
1046  }
1047 
1048 
1049 
1053 
1054  void column::set_foreign_key(const TString& _table, const TString& _column)
1055  {
1056  // declare this column to be a foreign key i.e. linked to the given
1057  // _column name in another _table
1058  fForeignKey = true;
1059  fFKtable = _table;
1060  fFKcolumn = _column;
1061  }
1062 
1063 
1064 
1068 
1069  void column::set_foreign_key(const table& _table, const column& _column)
1070  {
1071  // declare this column to be a foreign key i.e. linked to the given
1072  // _column name in another _table
1073  fForeignKey = true;
1074  fFKtable = _table.name();
1075  fFKcolumn = _column.name();
1076  }
1077 
1078 
1079 
1081 
1083  {
1088  }
1089 
1090 
1091 
1094 
1095  void table::show_columns() const
1096  {
1097  // print list of columns
1098  std::cout << "Columns in table:" << std::endl;
1099 #ifdef WITH_CPP11
1100  for (auto it = fColumns.begin(); it != fColumns.end(); ++it) {
1101 #else
1102  for (std::vector<KVSQLite::column>::const_iterator it = fColumns.begin(); it != fColumns.end(); ++it) {
1103 #endif
1104  std::cout << "\t" << it->name() << " [" << it->type_name() << "]" << std::endl;
1105  }
1106  }
1107 
1108 
1109 
1114 
1116  {
1117  // add column to table
1118  // \return reference to added column.
1119  // \note cannot be used for existing table in database: see database::add_column()
1120  fColumns.push_back(c);
1121  fColMap[c.name()] = c.index();
1122  fColumns.back().set_table(name());
1123  return fColumns.back();
1124  }
1125 
1126 
1127 
1132 
1134  {
1135  // add column to table
1136  // \return reference to added column
1137  // \note cannot be used for existing table in database: see database::add_column()
1138  return add_column(name, type_map[type]);
1139  }
1140 
1141 
1142 
1151 
1153  {
1154  // add a PRIMARY KEY column to the table
1155  // \returns reference to primary key (cannot be modified)
1156  //
1157  // \note by default this is an `INTEGER` type column
1158  //
1159  // \note as it is auto-incremented with each inserted row, it should not
1160  // be included in TSQLStatement used to write data to db
1161 
1163  c.set_constraint("PRIMARY KEY");
1164  return c;
1165  }
1166 
1167 
1168 
1176 
1177  const column& table::add_foreign_key(const TString& other_table, const TString& other_column)
1178  {
1179  // add a foreign key to the table, which is an INTEGER reference to
1180  // another column in another table
1181  // \returns reference to key (cannot be modified)
1182  //
1183  // \note as foreign keys are only really useful if they have the same name in the child and parent tables,
1184  // we set the name of the foreign key by default to that of the other_column
1185 
1187  c.set_foreign_key(other_table, other_column);
1188  return c;
1189  }
1190 
1191 
1192 
1200 
1201  const column& table::add_foreign_key(const table& other_table, const column& other_column)
1202  {
1203  // add a foreign key to the table, which is an INTEGER reference to
1204  // another column in another table.
1205  // \returns reference to key (cannot be modified)
1206  //
1207  // \note as foreign keys are only really useful if they have the same name in the child and parent tables,
1208  // we set the name of the foreign key by default to that of the other_column
1209 
1211  c.set_foreign_key(other_table, other_column);
1212  return c;
1213  }
1214 
1215 
1216 
1220 
1222  {
1223  // make sure that all parameters in the list have corresponding columns in the table
1224  // \returns the number of columns to be added
1225 
1226  int ncols = 0;
1227  int ipar = l.GetNpar();
1228  for (int i = 0; i < ipar; ++i) {
1229  KVNamedParameter* par = l.GetParameter(i);
1230  if (!has_column(par->GetName())) ncols++;
1231  }
1232  return ncols;
1233  }
1234 
1235 
1236 
1243 
1244  void table::prepare_data(const KVNameValueList& l, const KVNamedParameter* null_value)
1245  {
1246  // fill all columns in table with data contained in KVNameValueList parameters having the same name.
1247  //
1248  // any columns which do not appear in the KVNameValueList (except for PRIMARY KEY) will be set to 'null'
1249  //
1250  // if required, any parameters with the same type&value as "null_value" will be set to 'null' too
1251 
1252  for (int i = 0; i < number_of_columns(); ++i) {
1253  KVNamedParameter* p = l.FindParameter((*this)[i].name());
1254  if (p && !(null_value && p->HasSameValueAs(*null_value)))
1255  (*this)[i].set_data(*p);
1256  else
1257  (*this)[i].set_null();
1258  }
1259  }
1260 
1261 
1262 
1265 
1267  {
1268  // set the value of all columns in the table to NULL
1269  for (int i = 0; i < number_of_columns(); ++i) {
1270  (*this)[i].set_null();
1271  }
1272  }
1273 
1274 
1275 
1281 
1282  TString table::get_column_names(const TString& exclude, const TString& delim) const
1283  {
1284  // Return a comma-separated list of the colum names
1285  //
1286  // \param[in] delim separator to use in list (default: ",")
1287  // \param[in] exclude list of column names to exclude from list
1288 
1289  TString namelist;
1290  int added = 0;
1291  for (int i = 0; i < number_of_columns(); ++i) {
1292  TString name = (*this)[i].name();
1293  if (exclude.Contains(name)) continue;
1294  if (added) namelist += delim;
1295  namelist += name;
1296  ++added;
1297  }
1298  return namelist;
1299  }
1300 
1301 
1302  //____________________________________________________________________________//
1303 
1304 }
1305 
Defines macros for standard error messages.
ClassImp(KVPartitionList) void KVPartitionList
Initialisation.
ROOT::R::TRInterface & r
#define f(i)
#define c(i)
const Bool_t kFALSE
const Bool_t kTRUE
int type
char * Form(const char *fmt,...)
R__EXTERN TSystem * gSystem
Handles lists of named parameters with different types, a list of KVNamedParameter objects.
void SetValue(const Char_t *name, value_type value)
A generic named parameter storing values of different types.
Bool_t HasSameValueAs(const KVNamedParameter &) const
const Char_t * GetString() const
void Set(const char *, const char *)
Int_t GetInt() const
const Char_t * GetSQLType() const
Double_t GetDouble() const
Strings used to represent a set of ranges of values.
Definition: KVNumberList.h:83
void Add(Int_t)
Add value 'n' to the list.
Long_t fBlobSize
binary data
Definition: SQLiteDB.h:122
void init_type_map()
Definition: SQLiteDB.cpp:932
void set_data_from_statement(TSQLStatement *s, int idx=-1) const
Definition: SQLiteDB.cpp:1010
const char * _type()
Definition: SQLiteDB.cpp:944
const char * type_name() const
Definition: SQLiteDB.h:156
std::pair< TString, KVSQLite_column_type > fNameType
Definition: SQLiteDB.h:116
static std::map< KVSQLite::column_type::types, TString > inv_type_map
Definition: SQLiteDB.h:119
void set_data_in_statement(TSQLStatement *, int idx=-1) const
Definition: SQLiteDB.cpp:967
const char * name() const
Definition: SQLiteDB.h:148
void set_foreign_key(const TString &_table, const TString &_column)
Definition: SQLiteDB.cpp:1054
TString fFKcolumn
Definition: SQLiteDB.h:126
KVNamedParameter fData
Definition: SQLiteDB.h:120
const char * get_declaration() const
return declaration for column, including type & constraint
Definition: SQLiteDB.cpp:360
T get_data() const
Definition: SQLiteDB.h:248
KVSQLite_column_type type() const
Definition: SQLiteDB.h:152
int index() const
Definition: SQLiteDB.h:171
TString fFKtable
Definition: SQLiteDB.h:125
TString fConstraint
Definition: SQLiteDB.h:117
const KVNamedParameter & data() const
Definition: SQLiteDB.h:243
void set_data(const T &x)
Definition: SQLiteDB.h:181
void set_constraint(const TString &c)
Definition: SQLiteDB.h:220
Interface to ROOT SQLite database backend ,.
Definition: SQLiteDB.h:389
void copy_table_data(const TString &source, const TString &destination, const TString &columns="*", const TString &selection="")
Definition: SQLiteDB.cpp:903
std::list< const column * > fSQLstmtCols
Definition: SQLiteDB.h:393
int count(const TString &table, const TString &column="*", const TString &selection="", bool distinct=false) const
Definition: SQLiteDB.cpp:739
TGraph * create_graph(const TString &tablename, const TString &Xcolumn, const TString &Ycolumn, const TString &selection="")
Definition: SQLiteDB.cpp:699
std::unique_ptr< TSQLResult > SelectRowsFromTable(const TString &table, const TString &columns="*", const TString &condition="") const
Definition: SQLiteDB.cpp:34
std::unique_ptr< TSQLStatement > fSQLstmt
Definition: SQLiteDB.h:392
bool update(const TString &table, const TString &columns, const TString &selection="")
Definition: SQLiteDB.cpp:776
void delete_data(const TString &table, const TString &selection="")
Definition: SQLiteDB.cpp:834
void show_tables() const
print list of tables
Definition: SQLiteDB.cpp:84
column & add_column(const TString &table, const TString &name, const TString &type)
Definition: SQLiteDB.cpp:855
void print_selection(const TString &table, const TString &columns, const TString &condition, int column_width=20) const
Print on stdout contents of database.
Definition: SQLiteDB.cpp:192
std::unique_ptr< TSQLiteServer > fDBserv
Definition: SQLiteDB.h:390
void insert_data_row()
Definition: SQLiteDB.cpp:393
void open(const TString &dbfile)
Definition: SQLiteDB.cpp:110
void end_data_insertion()
Definition: SQLiteDB.cpp:433
bool select_data(const TString &tables, const TString &columns="*", const TString &selection="", bool distinct=false, const TString &anything_else="") const
Definition: SQLiteDB.cpp:493
KVNameValueList get_name_value_list(const TString &table, const TString &name_column, const TString &value_column, const TString &selection="", const TString &anything_else="")
Definition: SQLiteDB.cpp:675
bool get_next_result() const
Definition: SQLiteDB.cpp:595
TString get_string_list(const TString &table, const TString &column, const TString &selection="", const TString &anything_else="")
Definition: SQLiteDB.cpp:651
void Dump() const
Print on stdout contents of database.
Definition: SQLiteDB.cpp:171
std::unordered_map< std::string, KVSQLite::table > fTables
Definition: SQLiteDB.h:391
void add_table(const table &)
Definition: SQLiteDB.cpp:219
void PrintResults(TSQLResult *tabent, int column_width=20) const
Definition: SQLiteDB.cpp:143
bool prepare_data_insertion(const TString &)
Definition: SQLiteDB.cpp:269
void clear_table(const TString &name)
Delete all data from table.
Definition: SQLiteDB.cpp:723
void print_selected_data(const TString &tables, const TString &columns="*", const TString &selection="", bool distinct=false, const TString &anything_else="")
Print out results of a call to select_data().
Definition: SQLiteDB.cpp:458
table * fBulkTable
Definition: SQLiteDB.h:394
void read_table_infos()
initialise map of database tables from existing database
Definition: SQLiteDB.cpp:52
void add_missing_columns(const TString &table, const KVNameValueList &l)
Definition: SQLiteDB.cpp:870
KVNumberList get_integer_list(const TString &table, const TString &column, const TString &selection="", const TString &anything_else="")
Definition: SQLiteDB.cpp:630
static std::map< TString, KVSQLite::column_type::types > type_map
Definition: SQLiteDB.h:267
const char * name() const
Definition: SQLiteDB.h:286
bool is_temporary() const
Definition: SQLiteDB.h:320
const column & add_primary_key(const TString &name)
Definition: SQLiteDB.cpp:1152
void set_all_columns_null()
set the value of all columns in the table to NULL
Definition: SQLiteDB.cpp:1266
std::vector< KVSQLite::column > fColumns
Definition: SQLiteDB.h:265
void show_columns() const
print list of columns
Definition: SQLiteDB.cpp:1095
void init_type_map()
Definition: SQLiteDB.cpp:1082
TString get_column_names(const TString &exclude="", const TString &delim=",") const
Definition: SQLiteDB.cpp:1282
column & add_column(const KVSQLite::column &c)
Definition: SQLiteDB.cpp:1115
int check_columns(const KVNameValueList &)
Definition: SQLiteDB.cpp:1221
int number_of_columns() const
Definition: SQLiteDB.h:376
bool has_column(const TString &name) const
Definition: SQLiteDB.h:347
const column & add_foreign_key(const TString &other_table, const TString &other_column)
Definition: SQLiteDB.cpp:1177
void prepare_data(const KVNameValueList &, const KVNamedParameter *=nullptr)
Definition: SQLiteDB.cpp:1244
const char * get_insert_command() const
Definition: SQLiteDB.cpp:336
KVSQLite_insert_mode fInsert
Definition: SQLiteDB.h:264
std::unordered_map< std::string, int > fColMap
Definition: SQLiteDB.h:266
Extension of ROOT TString class which allows backwards compatibility with ROOT v3....
Definition: KVString.h:72
void Begin(TString delim) const
Definition: KVString.cpp:565
Bool_t End() const
Definition: KVString.cpp:634
KVString Next(Bool_t strip_whitespace=kFALSE) const
Definition: KVString.cpp:695
virtual const char * GetName() const
virtual const char * GetName() const
virtual Int_t GetFieldCount()=0
virtual const char * GetFieldName(Int_t field)=0
virtual TSQLRow * Next()=0
static TSQLServer * Connect(const char *db, const char *uid, const char *pw)
Int_t Atoi() const
const char * Data() const
void Form(const char *fmt,...)
Bool_t Contains(const char *pat, ECaseCompare cmp=kExact) const
virtual char * ExpandPathName(const char *path)
Double_t x[n]
for(Int_t i=0;i< n;i++)
const long double s
Definition: KVUnits.h:94
const long double g
masses
Definition: KVUnits.h:72
void Error(const char *location, const char *va_(fmt),...)
auto * l