KaliVeda
Toolkit for HIC analysis
SQLiteDB.cpp
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 "TSQLRow.h"
7 #include "TSQLTableInfo.h"
8 #include "TSQLColumnInfo.h"
9 #include <KVString.h>
10 #include <iostream>
11 #include "KVError.h"
12 #include <iomanip>
13 #include "TSystem.h"
14 #include <thread>
15 #include <chrono>
16 using namespace std::chrono_literals;
17 
21 
22 
23 namespace KVSQLite {
24 
25  bool database::debug = false;
26 
27  // static maps instantiation
28  std::map<TString, KVSQLite::column_type::types> table::type_map;
29  std::map<KVSQLite::column_type::types, TString> column::inv_type_map;
30 
31 
37 
38  std::unique_ptr<TSQLResult> database::SelectRowsFromTable(const TString& table, const TString& columns, const TString& condition) const
39  {
40  // \param[in] table name of table
41  // \param[in] columns comma-separated list of columns
42  // \param[in] condition selection to be applied, if any
43  // \returns result of query `SELECT [columns] FROM [table] WHERE [condition]`
44 
45  TString _query = "SELECT " + double_quote_all_tables_and_columns(columns) + " FROM " + double_quote_all_tables_and_columns(table);
46  if (condition != "") _query += " WHERE " + double_quote_all_tables_and_columns(condition);
47  return query(_query);
48  }
49 
50 
51 
54 
55  void database::read_table_infos()
56  {
57  // initialise map of database tables from existing database
58  std::unique_ptr<TList> tl(fDBserv->GetTablesList());
59  TObject* o;
60  TIter it_tab(tl.get());
61  while ((o = it_tab())) {
62 
63  table t(o->GetName());
64 
65  std::unique_ptr<TSQLResult> columnRes(fDBserv->GetColumns("", o->GetName()));
66  if (!columnRes.get()) {
67  Error("read_table_infos", "Cannot get information on columns for table %s", o->GetName());
68  return;
69  }
70  std::unique_ptr<TSQLRow> columnRow(columnRes->Next());
71  while ((columnRow.get()) != nullptr) {
72  column& col = t.add_column(columnRow->GetField(1), columnRow->GetField(2));
73  TString primary_key(columnRow->GetField(5));
74  if (primary_key.Atoi() == 1) t.primary_key(col.name());
75  columnRow.reset(columnRes->Next());
76  }
77  fTables.insert(std::pair<std::string, KVSQLite::table>(o->GetName(), t));
78  (*this)[t.name()].fDB = this;// set pointer to dB in table
79  }
80  // retrieve foreign key constraints for tables
81  for (auto& p : fTables) {
82  TString qry = Form("PRAGMA foreign_key_list('%s')", p.second.name());
83  // produces output like:
84  // id | seq | parent_table | child_key | parent_key | on_update | on_delete | match
85  auto result = query(qry);
86  std::unique_ptr<TSQLRow> row(result->Next());
87  while (row) {
88  TString parent_table(row->GetField(2));
89  TString child_key(row->GetField(3));
90  TString parent_key(row->GetField(4));
91  p.second.foreign_key(child_key, parent_table, parent_key);
92  row.reset(result->Next());
93  }
94  p.second.set_foreign_key_back_references();
95  }
96  }
97 
98 
99 
102 
103  void database::show_tables() const
104  {
105  // print list of tables
106  std::cout << "Tables in database:" << std::endl;
107  for (auto it = fTables.begin();
108  it != fTables.end(); ++it) {
109  std::cout << "\t" << it->first << std::endl;
110  }
111  }
112 
113 
114 
124 
125  void database::open(const TString& dbfile)
126  {
127  // Open/create sqlite db file given path
128  //
129  // Any special characters/environment variables are first expanded, so
130  // you can use:
131  //~~~~
132  // ~/mydata.db
133  // $(SOME_PATH)/toto.db
134  //~~~~
135 
136  TString exp_path = dbfile;
137  if (gSystem->ExpandPathName(exp_path)) {
138  Error("open", "problem with SQLite database filename: %s", dbfile.Data());
139  fIsValid = false;
140  return;
141  }
142  TString uri = "sqlite://" + exp_path;
143  fDBserv.reset(new KVSQLiteServer(uri, 0, 0));
144  // check for valid database file
145  if (!execute("pragma schema_version")) {
146  if (fDBserv->IsLocked()) {
147  // the db file has been locked by another process/thread
148  // sleep, then try again (5 times)
149  int i = 1;
150  do {
151  Info("open", "Database is locked. Try again in 1s...");
152  std::this_thread::sleep_for(1s);
153  Info("open", "Try number %d...", i);
154  ++i;
155  }
156  while (!execute("pragma schema_version") && fDBserv->IsLocked() && (i < 10));
157  if ((i < 10) && fDBserv->IsOK()) {
158  Info("open", "...database successfully opened");
159  fIsValid = true;
160  read_table_infos();
161  return;
162  }
163  }
164  fDBserv->Close();
165  fIsValid = false;
166  return;
167  }
168  fIsValid = true;
169  read_table_infos();
170  }
171 
172 
173 
175 
176  void database::PrintResults(TSQLResult* tabent, int column_width) const
177  {
178  int nfields = tabent->GetFieldCount();
179  std::unique_ptr<TSQLRow> row(nullptr);
180  for (int r = -1; true; ++r) {
181  if (r > -1) {
182  row.reset(tabent->Next());
183  if (row.get() == nullptr) break;
184  std::cout << std::setw(6) << r;
185  }
186  for (int f = 0; f < nfields; ++f) {
187  if (r < 0) {
188  if (f == 0) std::cout << std::setw(6) << "#";
189  std::cout << "|" << std::setw(column_width) << tabent->GetFieldName(f) ;
190  }
191  else {
192  std::cout << "|" << std::setw(column_width) << row->GetField(f) ;
193  }
194  }
195  std::cout << "\n";
196  }
197  }
198 
199 
200 
203 
204  void database::Dump() const
205  {
206  // Print on stdout contents of database
207 
208  std::cout << "Database : " << fDBserv->GetDB() << " [" << fDBserv->GetDBMS() << "]\n";
209  std::unique_ptr<TList> tl(fDBserv->GetTablesList());
210  TObject* o;
211  TIter it_tab(tl.get());
212  while ((o = it_tab())) {
213  std::cout << "\t";
214  std::cout << "Table : " << o->GetName() << "\n";
215  std::unique_ptr<TSQLResult> tabent = SelectRowsFromTable(o->GetName());
216  PrintResults(tabent.get());
217  }
218  std::cout << std::endl;
219  }
220 
221 
224 
225  void database::print_selection(const TString& table, const TString& columns, const TString& condition, int column_width) const
226  {
227  // Print on stdout contents of database
228 
229  std::unique_ptr<TSQLResult> tabent = SelectRowsFromTable(table, columns, condition);
230  PrintResults(tabent.get(), column_width);
231  std::cout << std::endl;
232  }
233 
234 
235 
253 
254  table& database::add_table(const table& t)
255  {
256  // add table to database (if it does not exist already)
257  //
258  // returns reference to new table in database
259  //
260  // _WARNING:_ after calling this method, do not use the table given as argument
261  //
262  // it does not correspond to the table in the database
263  //
264  // instead use `db["table name"]` or the reference returned by this method to access the table
265  //
266  //~~~~{.cpp}
267  // e.g. KVSQLite::table tt("some table");
268  // tt.add_column(...);
269  // db.add_table(tt);
270  // db["some table"]["column"].set_data(...);
271  //~~~~
272 
273  auto command = t.get_table_creation_command();
274  if (debug)
275  std::cout << command << std::endl;
276  if (execute(command)) {
277  fTables.insert(std::pair<std::string, KVSQLite::table>(t.name(), t));
278  (*this)[t.name()].fDB = this;// set pointer to dB in table
279  (*this)[t.name()].set_foreign_key_back_references();
280  }
281  return (*this)[t.name()];
282  }
283 
284 
285 
302 
303  bool database::prepare_data_insertion(const TString& table)
304  {
305  // Call this method before insert_data_row() in order to perform bulk data
306  // insertion operation. Note that this can only be used in cases where values are set for
307  // each column (apart from, eventually, a rowid alias column i.e. an INTEGER PRIMARY KEY)
308  // before insertion. If not, use prepare_data_insertion_selected_columns().
309  //
310  //~~~~{.cpp}
311  // db.prepare_data_insertion("my_table");
312  // while(...){ // loop over data to insert
313  // // set up data in table
314  // db.insert_data_row();
315  // }
316  // db.end_data_insertion(); // terminate data insertion
317  //~~~~
318  // Until method end_data_insertion() is called, you cannot call prepare_data_insertion()
319  // with a different table name.
320 
321  if (debug) Info("prepare_data_insertion", "table=%s", table.Data());
322  if (fInserting) {
323  if (fBulkTable) {
324  Error("database::prepare_data_insertion",
325  "bulk insertion in progress for table %s; call database::commit() to terminate transaction",
326  fBulkTable->name());
327  return false;
328  }
329  else {
330  Error("database::prepare_data_insertion",
331  "bulk insertion in progress; call database::commit() to terminate transaction");
332  return false;
333  }
334  }
335  if (fSelecting) {
336  Error("database::prepare_data_insertion",
337  "data retrieval in progress; call get_next_result() until it returns false");
338  return false;
339  }
340  fInserting = true;
341  fDBserv->StartTransaction();
342  // set up SQL statement for data insertion into table
343  fBulkTable = &fTables[table.Data()];
344  TString com(fBulkTable->get_insert_command());
345  int ncol = fBulkTable->number_of_columns();
346  int idx = 0;
347  for (int i = 0; i < ncol; ++i) {
348  if (idx) com += ",";
349  if (!(*fBulkTable)[i].rowid_alias()) {
350  com += Form("\"%s\"", (*fBulkTable)[i].name());;
351  ++idx;
352  }
353  }
354  com += ") VALUES (";
355  idx = 0;
356  for (int i = 0; i < ncol; ++i) {
357  if (idx) com += ",";
358  if (!(*fBulkTable)[i].rowid_alias()) {
359  com += "?";
360  ++idx;
361  }
362  }
363  com += ")";
364  if (debug) std::cout << com << std::endl;
365  return prepare_statement(com);
366  }
367 
368 
369 
394 
395  bool database::prepare_data_insertion_selected_columns(const TString& table)
396  {
397  // Call this method before insert_data_row_selected_columns() in order to perform bulk data
398  // insertion operation without giving values for all columns (apart from an eventual rowid alias column).
399  //
400  //~~~~{.cpp}
401  // // initialise columns
402  // db["my_table"].prepare_insert_single_row();
403  // // set data in columns for first row to insert
404  // db["my_table"]["colA"] = "ok";
405  // db["my_table"]["colC"] = 116;
406  // // set up bulk insertion
407  // db.prepare_data_insertion_selected_columns("my_table");
408  // // insert first row of data
409  // db.insert_data_row();
410  // while(...){ // loop over rest of data to insert
411  // // set up data in table
412  // db["my_table"]["colA"] = "bad";
413  // db["my_table"]["colC"] = -356;
414  // db.insert_data_row();
415  // }
416  // db.end_data_insertion(); // terminate data insertion
417  //~~~~
418  // Until method end_data_insertion() is called, you cannot call prepare_data_insertion()
419  // with a different table name.
420 
421  if (debug) Info("prepare_data_insertion_selected_columns", "table=%s", table.Data());
422  if (fInserting) {
423  if (fBulkTable) {
424  Error("database::prepare_data_insertion",
425  "bulk insertion in progress for table %s; call database::commit() to terminate transaction",
426  fBulkTable->name());
427  return false;
428  }
429  else {
430  Error("database::prepare_data_insertion",
431  "bulk insertion in progress; call database::commit() to terminate transaction");
432  return false;
433  }
434  }
435  if (fSelecting) {
436  Error("database::prepare_data_insertion",
437  "data retrieval in progress; call get_next_result() until it returns false");
438  return false;
439  }
440  fInserting = true;
441  fDBserv->StartTransaction();
442  // set up SQL statement for data insertion into table
443  fBulkTable = &fTables[table.Data()];
444  TString com(fBulkTable->get_insert_command());
445  int ncol = fBulkTable->number_of_columns();
446  int idx = 0;
447  for (int i = 0; i < ncol; ++i) {
448  if (idx) com += ",";
449  if (!(*fBulkTable)[i].rowid_alias() && (*fBulkTable)[i].has_data) {
450  com += Form("\"%s\"", (*fBulkTable)[i].name());;
451  ++idx;
452  }
453  }
454  com += ") VALUES (";
455  idx = 0;
456  for (int i = 0; i < ncol; ++i) {
457  if (idx) com += ",";
458  if (!(*fBulkTable)[i].rowid_alias() && (*fBulkTable)[i].has_data) {
459  com += "?";
460  ++idx;
461  }
462  }
463  com += ")";
464  if (debug) std::cout << com << std::endl;
465  return prepare_statement(com);
466  }
467 
468 
469 
471 
472  const char* table::get_insert_command() const
473  {
474  switch (fInsert) {
476  return Form("INSERT OR FAIL INTO \"%s\"(", name());
477  break;
479  return Form("INSERT OR IGNORE INTO \"%s\"(", name());
480  break;
482  return Form("INSERT OR REPLACE INTO \"%s\"(", name());
483  break;
484  default:
486  break;
487  }
488  return Form("INSERT INTO \"%s\"(", name());
489  }
490 
491 
492 
498 
499  TString column::get_declaration() const
500  {
501  // return declaration for column, including type & constraint
502  //
503  // the name of the column is enclosed in double-quotes, in case it is the
504  // same as an SQL keyword
505 
506  TString decl;
507  decl.Form("\"%s\" %s", name(), TypeName().Data());
508  if (!fConstraint.IsNull()) {
509  decl += " ";
510  decl += fConstraint;
511  }
512  return decl;
513  }
514 
515 
516 
528 
529  void database::insert_data_row()
530  {
531  // Call (repeatedly) after a call to prepare_data_insertion()
532  // in order to insert current contents of table columns as a new row in the database.
533  //
534  // Value of each column should first be set like this:
535  //
536  //~~~~{.cpp}
537  // db["table"]["id"].set_data(6);
538  // db["table"]["name"] = "triumph";
539  //~~~~
540  // Call end_data_insertion() when all data has been inserted
541 
542  if (!fInserting || !fBulkTable) {
543  Error("database::insert_data_row",
544  "no transaction initialized; call prepare_data_insertion(name_table) before this method");
545  return;
546  }
547  if (fSelecting) {
548  Error("database::insert_data_row",
549  "data retrieval in progress; call get_next_result() until it returns false, then call prepare_data_insertion(name_table) before this method");
550  return;
551  }
552  int ncol = fBulkTable->number_of_columns();
553  fSQLstmt->NextIteration();
554  int idx = 0;
555  for (int i = 0; i < ncol; ++i) {
556  if (!(*fBulkTable)[i].rowid_alias()) {
557  (*fBulkTable)[i].set_data_in_statement(fSQLstmt.get(), idx);
558  ++idx;
559  }
560  }
561  }
562 
563 
575 
576  void database::insert_data_row_selected_columns()
577  {
578  // Call (repeatedly) after a call to prepare_data_insertion()
579  // in order to insert current contents of table columns as a new row in the database.
580  //
581  // Value of each column should first be set like this:
582  //
583  //~~~~{.cpp}
584  // db["table"]["id"].set_data(6);
585  // db["table"]["name"] = "triumph";
586  //~~~~
587  // Call end_data_insertion() when all data has been inserted
588 
589  if (!fInserting || !fBulkTable) {
590  Error("database::insert_data_row",
591  "no transaction initialized; call prepare_data_insertion(name_table) before this method");
592  return;
593  }
594  if (fSelecting) {
595  Error("database::insert_data_row",
596  "data retrieval in progress; call get_next_result() until it returns false, then call prepare_data_insertion(name_table) before this method");
597  return;
598  }
599  int ncol = fBulkTable->number_of_columns();
600  fSQLstmt->NextIteration();
601  int idx = 0;
602  for (int i = 0; i < ncol; ++i) {
603  if (!(*fBulkTable)[i].rowid_alias() && (*fBulkTable)[i].has_data) {
604  (*fBulkTable)[i].set_data_in_statement(fSQLstmt.get(), idx);
605  ++idx;
606  }
607  }
608  }
609 
610 
611 
615 
616  void database::end_data_insertion()
617  {
618  // Call after prepare_data_insertion() & insert_data_row() have been
619  // used to insert data into a table
620 
621  if (debug) Info("end_data_insertion", "now");
622  if (!fInserting) {
623  Error("database::end_data_insertion",
624  "no transaction initialized; call prepare_data_insertion(name_table) first");
625  return;
626  }
627  if (fSelecting) {
628  Error("database::insert_data_row",
629  "data retrieval in progress; call get_next_result() until it returns false, then call prepare_data_insertion(name_table) before this method");
630  return;
631  }
632  fBulkTable = nullptr;
633  fSQLstmt->Process();
634  fDBserv->Commit();
635  fInserting = false;
636  }
637 
638 
641 
642  void database::print_selected_data(const TString& tables, const TString& columns, const TString& selection, bool distinct, const TString& anything_else)
643  {
644  // Print out results of a call to select_data().
645 
646  if (select_data(tables, columns, selection, distinct, anything_else)) {
647  // retrieve column titles (with table name) and store length of each
648  std::vector<TString> col_titles;
649  std::vector<int> col_width;
650  std::for_each(std::begin(fSQLstmtCols), std::end(fSQLstmtCols),
651  [&col_titles, &col_width]
652  (const column * col) {
653  col_titles.push_back(col->get_table() + "::" + col->Name());
654  col_width.push_back(col->get_table().Length() + 2 + col->Name().Length());
655  }
656  );
657  // retrieve column data and update maximum length of column
658  using data_row = std::vector<TString>;
659  std::vector<data_row> col_data;
660  while (get_next_result()) {
661  data_row current_data_row;
662  int col_index = 0;
663  std::for_each(std::begin(fSQLstmtCols), std::end(fSQLstmtCols),
664  [&col_index, &col_width, &current_data_row]
665  (const column * col) {
666  current_data_row.push_back(col->data().GetTString());
667  col_width[col_index] = std::max(current_data_row[col_index].Length(), col_width[col_index]);
668  ++col_index;
669  }
670  );
671  col_data.push_back(current_data_row);
672  }
673  auto print_line = [&col_width]() {
674  for (auto width : col_width) {
675  std::cout << "+-";
676  for (int i = 0; i <= width; ++i) std::cout << "-";
677  }
678  std::cout << "+\n";
679  };
680  auto print_column_item = [](const TString & item, int width, bool end_line = false) {
681  std::cout << "| " << item;
682  for (int i = 0; i <= width - item.Length(); ++i)
683  std::cout << " ";
684  if (end_line)
685  std::cout << "|";
686  };
687  auto print_column_data = [&col_width, &print_column_item](const std::vector<TString>& cols) {
688  int col_index = 0;
689  int ncols = cols.size();
690  for (auto& col : cols) {
691  print_column_item(col, col_width[col_index], (col_index == ncols - 1));
692  ++col_index;
693  }
694  std::cout << std::endl;
695  };
696  print_line();
697  print_column_data(col_titles);
698  print_line();
699  for (auto& row : col_data) print_column_data(row);
700  print_line();
701  }
702  }
703 
704 
705 
707 
708  std::list<const table*> database::generate_table_selection_string(const TString& tables, KVString& table_selection) const
709  {
710  std::list<const table*> table_list;
711  KVString _tables(tables);
712  _tables.Begin(",");
713  // use double-quoted table names in table_selection
714  int i(0);
715  std::unordered_map<std::string, int> table_names;
716  while (!_tables.End()) {
717  TString tabnam = _tables.Next();
718  table_list.push_back(& operator[](tabnam));
719  if (i) {
720  table_selection += " INNER JOIN \"" + tabnam + "\"";
721  // add any foreign key constraints involving any previously mentioned table
722  auto this_table = table_list.back();
723  if (this_table->has_foreign_keys(false)) { // include back references
724  TString on_and = "ON";
725  for (auto& f : this_table->fForeignKeys) {
726  // make sure other table is already in list
727  if (table_names[f.parent_table.Data()]) {
728  table_selection += Form(" %s %s.%s = %s.%s",
729  on_and.Data(),
730  this_table->name(), f.child_key.Data(),
731  f.parent_table.Data(), f.parent_key.Data());
732  on_and = "AND"; // chain successive foreign key constraints: 'ON a=b AND c=d AND ...'
733  }
734  }
735  }
736  }
737  else
738  table_selection += "\"" + tabnam + "\"";
739  ++table_names[tabnam.Data()];
740  ++i;
741  }
742  return table_list;
743  }
744 
745 
746 
748 
750  {
751  auto replacer = [&](const KVString & repl) {
752  KVString replaced;
753  // treat each whitespace-separated token in string separately
754  repl.Begin(" ");
755  while (!repl.End()) {
756  auto tok = repl.Next();
757  if (replaced.Length()) replaced += " ";
758  bool stop_replacing = false;
759  for (auto& tp : fTables) {
760  TString table_name = tp.second.name();
761  if (tok == table_name) {
762  tok.Prepend("\"");
763  tok.Append("\"");
764  stop_replacing = true;
765  break;
766  }
767  for (auto& c : tp.second.fColumns) {
768  TString column_name = c.name();
769  if (tok == column_name) {
770  tok.Prepend("\"");
771  tok.Append("\"");
772  stop_replacing = true;
773  break;
774  }
775  }
776  if (stop_replacing) break;
777  }
778  replaced += tok;
779  }
780  return replaced;
781  };
782  // no replacements inside string constants i.e. between pairs of ''
783  KVString _input(input);
784  _input.Begin("'");
785  bool in_quotes = _input.BeginsWith("'");
786  KVString output;
787  while (!_input.End()) {
788  auto part = _input.Next();
789  if (!in_quotes) {
790  output += replacer(part);
791  }
792  else {
793  output += "'" + part + "'";
794  }
795  in_quotes = !in_quotes;
796  }
797  return output;
798  }
799 
800 
801 
821 
822  bool database::select_data(const TString& tables, const TString& columns, const TString& selection, bool distinct, const TString& anything_else) const
823  {
824  // Select data in database from given table(s) according to
825  //~~~~
826  // SELECT [columns] FROM [tables] WHERE [selection] [anything_else]
827  //~~~~
828  // In order to retrieve results, call get_next_result() until it returns false.
829  //
830  // \param tables if more than 1 table is given, separate table names with commas.
831  // if 1 table has a foreign key referencing the other, this allows to JOIN data
832  // in both tables together. [columns] can then refer to columns in either table.
833  // \param columns ="*" by default, i.e. data from all columns is retrieved.
834  // If specific column data is to be selected, give a comma-separated list of
835  // column names. These will be quoted correctly in case they contain spaces.
836  // \param distinct can be used in conjunction with a selection of specific columns in order
837  // to retrieve only rows of data with different values for the column(s).
838  //
839  // You can use the COUNT() function as one of the column names (upper or lower case).
840  // In this case, the value for each row can be retrieved by calling get_count_column_value()
841  // after each call to get_next_value().
842 
843  if (debug) Info("select_data", "tables=%s columns=%s selection=%s anything_else=%s",
844  tables.Data(), columns.Data(), selection.Data(), anything_else.Data());
845  if (fInserting) {
846  Error("database::select_data",
847  "data insertion in progress; call end_data_insertion() before retrieving data");
848  return false;
849  }
850  if (fSelecting) {
851  Error("database::select_data",
852  "data retrieval already in progress; call get_next_result() until it returns false before making new selection");
853  return false;
854  }
855 
856  fSQLstmtCols.clear();
857 
858  KVString table_selection;
859  std::list<const table*> table_list = generate_table_selection_string(tables, table_selection);
860 
861  KVString column_selection(""), _columns(columns);
862  if (columns == "*") {
863  column_selection = "*";
864  distinct = false; // don't allow 'SELECT DISTINCT * FROM ....' (?)
865  // find right column in right table for each item
866  // loop over all columns in each table
867  for (auto tabs : table_list) {
868  for (int i = 0; i < tabs->number_of_columns(); ++i) {
869  fSQLstmtCols.push_back(&(*tabs)[i]);
870  }
871  }
872  }
873  else {
874  if (distinct) column_selection = "DISTINCT ";
875  _columns.Begin(",");
876  int i(0);
877  while (!_columns.End()) {
878  TString colnam = _columns.Next();
879  if (i) column_selection += ", ";
880  ++i;
881  // find right column in right table for this item
882  TString COLNAM = colnam;
883  COLNAM.ToUpper();
884  if (COLNAM.BeginsWith("COUNT(")) { // look for use of COUNT()
885  // no double quotes around COUNT()!
886  column_selection += Form("%s", colnam.Data());
887  fSQLstmtCols.push_back(&count_column);
888  }
889  else {
890  // put double-quoted column names in column_selection
891  column_selection += Form("\"%s\"", colnam.Data());
892  for (auto tabs : table_list)
893  if (tabs->has_column(colnam)) fSQLstmtCols.push_back(&(*tabs)[colnam]);
894  }
895  }
896  }
897 
898  TString cond = Form("SELECT %s FROM %s", column_selection.Data(), table_selection.Data());
899  if (selection != "") {
900  // make sure any column names or table names are double quoted
901  cond += " WHERE " + double_quote_all_tables_and_columns(selection);
902  }
903  if (anything_else != "") {
904  // make sure any column names or table names are double quoted
905  cond += " " + double_quote_all_tables_and_columns(anything_else);
906  }
907  if (debug)
908  std::cout << cond << std::endl;
909  prepare_statement(cond);
910  if (fSQLstmt.get() == nullptr) {
911  Error("database::select_data", "problem processing : %s", cond.Data());
912  fSelecting = false;
913  fBulkTable = nullptr;
914  return false;
915  }
916  fSQLstmt->EnableErrorOutput();
917  if (fSQLstmt->Process()) {
918  fSQLstmt->StoreResult();
919  fSelecting = true;
920  fEmptyResultSet = false;
921  return true;
922  }
923  else if (!fSQLstmt->IsError()) {
924  // query ok, no results correspond to selection
925  fSQLstmt->StoreResult();
926  fSelecting = true;
927  fEmptyResultSet = true;
928  return true;
929  }
930  fSelecting = false;
931  fBulkTable = nullptr;
932  return false;
933  }
934 
935 
936 
942 
943  bool database::select_data_in_list(const TString& tables, const KVNameValueList& selection, const TString& columns, bool distinct, const TString& anything_else) const
944  {
945  // Select data according to the name-value pairs in the KVNameValueList. Each name in the list is assumed to correspond to a column name,
946  // with a corresponding value of the correct type.
947  //
948  // For other arguments, see the other select_data() method.
949 
950  return select_data(tables, columns, selection.AsSQLSelection(), distinct, anything_else);
951  }
952 
953 
954 
958 
959  bool database::get_next_result() const
960  {
961  // Retrieve next result row resulting from previous call to select_data()
962  // \returns kFALSE when no more data is retrieved
963 
964  if (debug) Info("get_next_result", "now");
965  if (fInserting) {
966  Error("database::get_next_result",
967  "data insertion in progress; call end_data_insertion() then select_data() before this method");
968  return false;
969  }
970  if (!fSelecting) {
971  Error("database::get_next_result",
972  "no data retrieval in progress; select_data() must be called and return true before calling this method");
973  return false;
974  }
975  if (!fEmptyResultSet && fSQLstmt->NextResultRow()) {
976  // set column data
977  int idx = 0;
978  for (auto col : fSQLstmtCols) {
979  col->set_data_from_statement(fSQLstmt.get(), idx);
980  ++idx;
981  }
982  return kTRUE;
983  }
984  fBulkTable = nullptr;
985  fSelecting = false;
986  return kFALSE;
987  }
988 
989 
990 
994 
995  KVNumberList database::get_integer_list(const TString& tables, const TString& column, const TString& selection, const TString& anything_else)
996  {
997  // \note Only for INTEGER columns!
998  // Fill KVNumberList with all `DISTINCT` values of "column" (only 1 column name at a time) for given selection
999 
1000  if (column.Contains(",")) {
1001  Error("database::get_integer_list", "Only 1 column name at a time please!");
1002  return {};
1003  }
1005  if (select_data(tables, column, selection, true, anything_else)) {
1006  auto column_data = fSQLstmtCols.front();
1007  while (get_next_result()) {
1008  result.Add(column_data->get_data<int>());
1009  }
1010  }
1011  return result;
1012  }
1013 
1014 
1015 
1020 
1021  TString database::get_string_list(const TString& tables, const TString& column, const TString& selection, const TString& anything_else)
1022  {
1023  // \note Only for TEXT columns!
1024  // Fill TString with comma-separated list of values of "column" (only 1 column name at a time) for given selection
1025  // \note Any NULL entries will be ignored
1026 
1027  if (column.Contains(",")) {
1028  Error("database::get_string_list", "Only 1 column name at a time please!");
1029  return "";
1030  }
1031  TString result;
1032  if (select_data(tables, column, selection, false, anything_else)) {
1033  auto column_data = fSQLstmtCols.front();
1034  while (get_next_result()) {
1035  if (column_data->is_null()) continue;
1036  if (result != "") result += ",";
1037  result += column_data->get_data<TString>();
1038  }
1039  }
1040  return result;
1041  }
1042 
1043 
1044 
1049 
1050  KVNameValueList database::get_name_value_list(const TString& tablename, const TString& name_column, const TString& value_column, const TString& selection, const TString& anything_else)
1051  {
1052  // Fill KVNameValueList with selected rows from table, adding for each row a parameter with the
1053  // name contained in "name_column" (must be of type `TEXT`) and the value contained in "value_column"
1054  // (can be `INTEGER`, `REAL`, or `TEXT`)
1055 
1057  if (select_data(tablename, Form("%s,%s", name_column.Data(), value_column.Data()), selection, false, anything_else)) {
1058  auto it = fSQLstmtCols.begin();
1059  auto nom = *it++;
1060  auto val = *it;
1061  while (get_next_result()) {
1062  result.SetValue(nom->get_data<TString>(), val->data());
1063  }
1064  }
1065  return result;
1066  }
1067 
1068 
1069 
1077 
1078  void database::get_name_value_list_for_each_row(const TString& tablename, std::function<bool (const KVNameValueList&)> callback, const TString& selection, const TString& anything_else)
1079  {
1080  // For each row from the table corresponding to given selection (if any), we fill a KVNameValueList with all
1081  // non-null column values, then call the user's callback function
1082  //~~~{.cpp}
1083  // bool my_callback(const KVNameValueList&);
1084  //~~~
1085  //with it. If the callback returns false, we stop the iteration (but finish the SQL query).
1086 
1087  bool call_callback = true;
1089  if (select_data(tablename, "*", selection, false, anything_else)) {
1090  while (get_next_result()) {
1091  for (auto col : fSQLstmtCols) {
1092  if (!col->is_null())
1093  result.SetValue(col->name(), col->data());
1094  }
1095  if (call_callback)
1096  call_callback = callback(result);
1097  }
1098  }
1099  }
1100 
1101 
1102 
1106 
1107  TGraph* database::create_graph(const TString& tablename, const TString& Xcolumn, const TString& Ycolumn, const TString& selection)
1108  {
1109  // Create and fill a TGraph from values Xcolumn and Ycolumn in table,
1110  // using the selection if required
1111 
1112  if (select_data(tablename, Form("%s,%s", Xcolumn.Data(), Ycolumn.Data()), selection)) {
1113  TGraph* g = new TGraph;
1114  int i = 0;
1115  auto it = fSQLstmtCols.begin();
1116  auto Xcol = *it++;
1117  auto Ycol = *it;
1118  while (get_next_result()) {
1119  g->SetPoint(i++, Xcol->get_data<double>(), Ycol->get_data<double>());
1120  }
1121  return g;
1122  }
1123  return nullptr;
1124  }
1125 
1126 
1127 
1130 
1131  void database::clear_table(const TString& name)
1132  {
1133  // Delete all rows in table, but does not remove table
1134  delete_rows_in_table(name);
1135  }
1136 
1137 
1138 
1161 
1162  int database::count(const TString& tables, const TString& column, const TString& selection, bool distinct, const TString& anything_else) const
1163  {
1164  // Returns number of rows corresponding to equivalent 'select_data' call
1165  //
1166  // + if column="*" all rows are included
1167  // + if a column name is given, only rows with a non-NULL value for column are counted
1168  // + if distinct=false, count all rows including those with the same value of column
1169  // + if distinct=true, count the number of different values of column
1170  //
1171  // Only 1 column name can be given as second argument
1172  //
1173  // Examples
1174  //
1175  //~~~
1176  // count("some_table")
1177  // => SELECT COUNT(*) FROM some_table;
1178  //
1179  // count("some_table", "some_column")
1180  // => SELECT COUNT(some_column) FROM some_table;
1181  //
1182  // count("some_table", "some_column", "", true)
1183  // => SELECT COUNT(DISTINCT some_column) FROM some_table;
1184  //~~~
1185 
1186  if (debug) Info("count", "tables=%s column=%s selection=%s anything_else=%s",
1187  tables.Data(), column.Data(), selection.Data(), anything_else.Data());
1188  TString qry = "SELECT count(";
1189  if (distinct && column != "*") qry += "DISTINCT ";
1190  if (column != "*") qry += Form("\"%s\"", column.Data());
1191  else qry += "*";
1192  qry += ") FROM ";
1193  KVString table_selection;
1194  generate_table_selection_string(tables, table_selection);
1195  qry += table_selection;
1196  if (selection != "") {
1197  qry += " WHERE " + double_quote_all_tables_and_columns(selection);
1198  }
1199  if (anything_else != "") {
1200  qry += " " + double_quote_all_tables_and_columns(anything_else);
1201  }
1202  if (debug) std::cout << qry << std::endl;
1203  auto result = query(qry);
1204  std::unique_ptr<TSQLRow> row(result->Next());
1205  TString number = row->GetField(0);
1206  return number.Atoi();
1207  }
1208 
1209 
1210 
1219 
1220  bool database::update(const TString& table, const TString& columns, const TString& selection)
1221  {
1222  // update the given columns of an entry in the table corresponding to selection (if given)
1223  // the current values of the data members of the columns will be used
1224  //
1225  // This is equivalent to
1226  //~~~~
1227  // UPDATE [table] SET col1=newval,col2=newval,... [WHERE [selection]]
1228  //~~~~
1229 
1230  if (debug) Info("update", "table=%s columns=%s selection=%s", table.Data(), columns.Data(), selection.Data());
1231  if (fInserting) {
1232  Error("database::update",
1233  "data insertion in progress; call end_data_insertion() before doing anything else");
1234  return false;
1235  }
1236  if (fSelecting) {
1237  Error("database::update",
1238  "data retrieval in progress; call get_next_result() until it returns false before doing anything else");
1239  return false;
1240  }
1241 
1242  fBulkTable = &fTables[table.Data()];
1243  TString query = Form("UPDATE \"%s\" SET ", table.Data());
1244  int ncol = fBulkTable->number_of_columns();
1245  int idx = 0;
1246  for (int i = 0; i < ncol; ++i) {
1247  if (columns.Contains((*fBulkTable)[i].name())) {
1248  if (idx) query += ",";
1249  query += Form("\"%s\"", (*fBulkTable)[i].name());
1250  query += "=?";
1251  ++idx;
1252  }
1253  }
1254  if (selection != "") query += " WHERE " + double_quote_all_tables_and_columns(selection);
1255  if (debug) std::cout << query << std::endl;
1256  if (!prepare_statement(query))
1257  return kFALSE;
1258  fSQLstmt->NextIteration();
1259  idx = 0;
1260  for (int i = 0; i < ncol; ++i) {
1261  if (columns.Contains((*fBulkTable)[i].name())) {
1262  (*fBulkTable)[i].set_data_in_statement(fSQLstmt.get(), idx);
1263  ++idx;
1264  }
1265  }
1266  return (fSQLstmt->Process());
1267  }
1268 
1269 
1270 
1277 
1278  void database::delete_table(const TString& table)
1279  {
1280  // Delete table from database. Equivalent to
1281  //
1282  //~~~~
1283  // DROP TABLE IF EXISTS table
1284  //~~~~
1285  TString query = Form("DROP TABLE IF EXISTS %s", table.Data());
1286  execute(query);
1287  }
1288 
1289 
1290 
1299 
1300  void database::delete_rows_in_table(const TString& table, const TString& selection)
1301  {
1302  // Delete rows from the table corresponding to selection
1303  //
1304  // This is equivalent to
1305  //~~~~
1306  // DELETE FROM [table] WHERE [selection]
1307  //~~~~
1308  // With no selection, deletes all rows of table (clear_table())
1309 
1310  if (debug) Info("delete_data", "table=%s selection=%s",
1311  table.Data(), selection.Data());
1312  TString query = Form("DELETE FROM \"%s\"", table.Data());
1313  if (selection != "") query += " WHERE " + double_quote_all_tables_and_columns(selection);
1314  execute(query);
1315  }
1316 
1317 
1318 
1322 
1323  column& database::add_column(const TString& table, const TString& name, const TString& type)
1324  {
1325  // add column to existing table
1326  // \returns reference to new column
1327  TString query = Form("ALTER TABLE \"%s\" ADD COLUMN \"%s\" %s", table.Data(), name.Data(), type.Data());
1328  execute(query);
1329  return (*this)[table].add_column(name, type);
1330  }
1331 
1332 
1333 
1337 
1338  void database::add_missing_columns(const TString& _table_, const KVNameValueList& l)
1339  {
1340  // add to table any columns which are defined in the list but don't exist
1341  // \note cannot be called during data insertion or retrieval!!!
1342 
1343  if (fInserting) {
1344  Error("database::add_missing_columns",
1345  "data insertion in progress; call end_data_insertion() before doing anything else");
1346  return;
1347  }
1348  if (fSelecting) {
1349  Error("database::add_missing_columns",
1350  "data retrieval in progress; call get_next_result() until it returns false before doing anything else");
1351  return;
1352  }
1353 
1354  table& tab = (*this)[_table_];
1355  for (auto& par : l)
1356  if (!tab.has_column(par.GetName())) add_column(_table_, par.GetName(), par.GetSQLType());
1357  }
1358 
1359 
1360 
1368 
1369  void database::copy_table_data(const TString& source, const TString& destination, const TString& columns, const TString& selection)
1370  {
1371  // Copy all selected data in 'source' table to 'destination'
1372  //
1373  // If the columns of the two tables are not identical, specify the columns to copy in 'columns'
1374  // (comma-separated list)
1375  // \note SQLite will not allow copy if the number of selected columns from 'source' is not
1376  // exactly equal to the number of columns in 'destination'
1377 
1378  TString COLUMNS = columns;
1379  if (COLUMNS != "*") {
1380  // quote all column names
1381  COLUMNS = "";
1382  KVString _columns(columns);
1383  _columns.Begin(",");
1384  while (!_columns.End()) {
1385  if (COLUMNS != "") COLUMNS += ", ";
1386  COLUMNS += Form("\"%s\"", _columns.Next(kTRUE).Data());
1387  }
1388  }
1389  TString query = Form("INSERT INTO \"%s\" SELECT %s FROM \"%s\"", destination.Data(), COLUMNS.Data(), source.Data());
1390  if (selection != "") query += " WHERE " + double_quote_all_tables_and_columns(selection);
1391  execute(query);
1392  }
1393 
1394 
1395 
1397 
1398  void column::init_type_map()
1399  {
1400  inv_type_map[KVSQLite::column_type::REAL] = "REAL";
1401  inv_type_map[KVSQLite::column_type::INTEGER] = "INTEGER";
1402  inv_type_map[KVSQLite::column_type::TEXT] = "TEXT";
1403  inv_type_map[KVSQLite::column_type::TIMESTAMP] = "TEXT";
1404  inv_type_map[KVSQLite::column_type::BLOB] = "BLOB";
1405  }
1406 
1407 
1408 
1410 
1411  TString column::_type()
1412  {
1413  return inv_type_map[fNameType.second];
1414  }
1415 
1416 
1417 
1419 
1420  template<> void column::set_data(const KVNamedParameter& x)
1421  {
1422  fData.Set(x.GetName(), x);
1423  fIsNull = false;
1424  has_data = true;
1425  }
1426 
1427 
1428 
1434 
1435  void column::set_data_in_statement(TSQLStatement* s, int idx) const
1436  {
1437  // set value of parameter in SQLite statement corresponding to this column
1438  // \param idx if given, use it as the statement parameter index instead of
1439  // the column's index in the table (case where not all columns are treated
1440  // in the statement)
1441 
1442  if (idx < 0) idx = index();
1443  if (fIsNull) {
1444  // null parameter
1445  s->SetNull(idx);
1446  return;
1447  }
1448  switch (type()) {
1450  s->SetDouble(idx, fData.GetDouble());
1451  break;
1453  s->SetInt(idx, fData.GetInt());
1454  break;
1457  s->SetString(idx, fData.GetString(), -1);
1458  break;
1460  s->SetBinary(idx, fBlob, fBlobSize);
1461  break;
1462  default:
1463  break;
1464  }
1465  }
1466 
1467 
1478 
1479  void column::set_data_from_statement(TSQLStatement* s, int idx) const
1480  {
1481  // set value of column according to value of parameter in statement
1482  //
1483  // any column which has a NULL value will be given value 0, 0.0 or ""
1484  // (for `INTEGER`, `REAL` or `TEXT` type, respectively): use column::is_null()
1485  // to check if this corresponds to a null column value.
1486  //
1487  // \param idx if given, use it as the statement parameter index instead of
1488  // the column's index in the table (case where not all columns are treated
1489  // in the statement)
1490 
1491  if (idx < 0) idx = index();
1492  fIsNull = s->IsNull(idx);
1493  switch (type()) {
1495  fData.Set(fIsNull ? 0.0 : s->GetDouble(idx));
1496  break;
1498  fData.Set(fIsNull ? 0 : s->GetInt(idx));
1499  break;
1502  fData.Set(fIsNull ? "" : s->GetString(idx));
1503  break;
1505  if (fIsNull) {
1506  fBlobSize = 0;
1507  }
1508  else {
1509  if (!fBlob) fBlob = (void*) new unsigned char[256];
1510  s->GetBinary(idx, fBlob, fBlobSize);
1511  }
1512  break;
1513  default:
1514  break;
1515  }
1516  }
1517 
1518 
1519 
1524 
1525  TString column::get_data_string_for_insert()
1526  {
1527  // write data in string; for TEXT data we enclose in single quotes
1528  //
1529  // we also replace any "'" with "''" (otherwise SQL error on insert)
1530  if (fData.IsString()) {
1531  auto r = fData.GetTString();
1532  r.ReplaceAll("''", "~#%"); // in case there are already double apostrophes
1533  r.ReplaceAll("'", "''");
1534  r.ReplaceAll("~#%", "''"); // in case there are already double apostrophes
1535  return Form("'%s'", r.Data());
1536  }
1537  return fData.GetTString();
1538  }
1539 
1540 
1541 
1543 
1544  void table::init_type_map()
1545  {
1546  type_map["REAL"] = KVSQLite::column_type::REAL;
1547  type_map["INTEGER"] = KVSQLite::column_type::INTEGER;
1548  type_map["TEXT"] = KVSQLite::column_type::TEXT;
1549  type_map["TIMESTAMP"] = KVSQLite::column_type::TIMESTAMP;
1550  type_map["BLOB"] = KVSQLite::column_type::BLOB;
1551  }
1552 
1553 
1554 
1560 
1561  void table::set_foreign_key_back_references()
1562  {
1563  // called by database::add_table
1564  //
1565  // for any foreign key defined for this table, we set up a back reference in
1566  // the parent table
1567 
1568  for (auto& f : fForeignKeys)
1569  fDB->get_table(f.parent_table).fForeignKeys.push_back({f.parent_key, name(), f.child_key, true});
1570  }
1571 
1572 
1573 
1586 
1587  TString table::get_table_creation_command() const
1588  {
1589  // Create and fill SQLite command to create this table, i.e.
1590  //
1591  //~~~
1592  // CREATE TABLE name (
1593  // col1 TYPE_1 [CONSTRAINT_1]
1594  // col2 TYPE_2 [CONSTRAINT_2]
1595  // ...
1596  // [PRIMARY KEY(col1,...)]
1597  // [FOREIGN KEY(col1,...) REFERENCES other_table(other_column) ...]
1598  // );
1599  //~~~
1600  TString command("CREATE ");
1601  if (is_temporary()) command += "TEMPORARY ";
1602  //command += "TABLE IF NOT EXISTS \"";
1603  command += "TABLE ";
1604  command += "\"" + fName + "\"";
1605  command += " (";
1606  for (int i = 0; i < number_of_columns(); ++i) {
1607  command += "\n ";
1608  command += fColumns[i].get_declaration();
1609  if (i < number_of_columns() - 1) command += ",";
1610  }
1611  if (has_primary_key()) {
1612  command += ",\n PRIMARY KEY(";
1613  command += "\"" + fPrimaryKey + "\"";
1614  command += ")";
1615  }
1616  if (has_foreign_keys()) {
1617  // foreign key constraints
1618  for (auto& fk : fForeignKeys) {
1619  command += ",\n FOREIGN KEY(";
1620  command += "\"" + fk.child_key + "\"";
1621  command += ") REFERENCES ";
1622  command += "\"" + fk.parent_table + "\"";
1623  if (!fk.parent_key.IsNull()) {
1624  command += "(";
1625  command += "\"" + fk.parent_key + "\"";
1626  command += ")";
1627  }
1628  }
1629  }
1630  command += "\n);";
1631  return command;
1632  }
1633 
1634 
1635 
1638 
1639  void table::show_columns() const
1640  {
1641  // print list of columns
1642  std::cout << "Columns in table:" << std::endl;
1643 #ifdef WITH_CPP11
1644  for (auto it = fColumns.begin(); it != fColumns.end(); ++it) {
1645 #else
1646  for (std::vector<KVSQLite::column>::const_iterator it = fColumns.begin(); it != fColumns.end(); ++it) {
1647 #endif
1648  std::cout << "\t" << it->name() << " [" << it->TypeName() << "]" << std::endl;
1649  }
1650  }
1651 
1652 
1653 
1658 
1659  column& table::add_column(const column& c)
1660  {
1661  // add column to table
1662  // \return reference to added column.
1663  // \note cannot be used for existing table in database: see database::add_column()
1664  fColumns.push_back(c);
1665  fColMap[c.name()] = c.index();
1666  fColumns.back().set_table(name());
1667  return fColumns.back();
1668  }
1669 
1670 
1671 
1676 
1677  column& table::add_column(const TString& name, const TString& type)
1678  {
1679  // add column to table
1680  // \return reference to added column
1681  // \note cannot be used for existing table in database: see database::add_column()
1682  return add_column(name, type_map[type]);
1683  }
1684 
1685 
1686 
1709 
1710  void table::foreign_key(const TString& child_key, const TString& parent_table, const TString& parent_key)
1711  {
1712  // add a FOREIGN KEY constraint to the table.
1713  //
1714  // The child key(s) should first be declared using add_column()
1715  //
1716  //~~~
1717  // foreign_key("item_id", "items", "id")
1718  //
1719  // => FOREIGN KEY(item_id) REFERENCES items(id)
1720  //
1721  // foreign_key("item_id", "items")
1722  //
1723  // => FOREIGN KEY(item_id) REFERENCES items
1724  //
1725  // [case where 'items' has an INTEGER PRIMARY KEY, no need to specify]
1726  //
1727  // foreign_key("songartist,songalbum", "album", "albumartist,albumname")
1728  //
1729  // => FOREIGN KEY(songartist, songalbum) REFERENCES album(albumartist, albumname)
1730  //
1731  // [case where both parent and child tables have composite primary keys]
1732  //~~~
1733 
1734  KVString check_columns = child_key;
1735  check_columns.Begin(",");
1736  bool ok = true;
1737  while (!check_columns.End()) {
1738  auto nxt = check_columns.Next(kTRUE);
1739  if (!has_column(nxt)) {
1740  Error("table::foreign_key", "Child key \"%s\" has not being declared with add_column()", nxt.Data());
1741  ok = false;
1742  }
1743  }
1744  if (!ok) return;
1745  fForeignKeys.push_back({child_key, parent_table, parent_key});
1746  }
1747 
1748 
1749 
1753 
1754  int table::check_columns(const KVNameValueList& l)
1755  {
1756  // make sure that all parameters in the list have corresponding columns in the table
1757  // \returns the number of columns to be added
1758 
1759  int ncols = 0;
1760  int ipar = l.GetNpar();
1761  for (int i = 0; i < ipar; ++i) {
1762  KVNamedParameter* par = l.GetParameter(i);
1763  if (!has_column(par->GetName())) ncols++;
1764  }
1765  return ncols;
1766  }
1767 
1768 
1769 
1776 
1777  void table::prepare_data(const KVNameValueList& l, const KVNamedParameter* null_value)
1778  {
1779  // fill all columns in table with data contained in KVNameValueList parameters having the same name.
1780  //
1781  // any columns which do not appear in the KVNameValueList (except for PRIMARY KEY) will be set to 'null'
1782  //
1783  // if required, any parameters with the same type&value as "null_value" will be set to 'null' too
1784 
1785  for (int i = 0; i < number_of_columns(); ++i) {
1786  KVNamedParameter* p = l.FindParameter((*this)[i].name());
1787  if (p && !(null_value && p->HasSameValueAs(*null_value)))
1788  (*this)[i].set_data(*p);
1789  else
1790  (*this)[i].set_null();
1791  }
1792  }
1793 
1794 
1795 
1798 
1799  void table::set_all_columns_null()
1800  {
1801  // set the value of all columns in the table to NULL
1802  for (int i = 0; i < number_of_columns(); ++i) {
1803  (*this)[i].set_null();
1804  }
1805  }
1806 
1807 
1808 
1814 
1815  TString table::get_column_names(const TString& exclude, const TString& delim) const
1816  {
1817  // Return a comma-separated list of the colum names
1818  //
1819  // \param[in] delim separator to use in list (default: ",")
1820  // \param[in] exclude list of column names to exclude from list
1821 
1822  TString namelist;
1823  int added = 0;
1824  for (int i = 0; i < number_of_columns(); ++i) {
1825  TString name = (*this)[i].name();
1826  if (exclude.Contains(name)) continue;
1827  if (added) namelist += delim;
1828  namelist += name;
1829  ++added;
1830  }
1831  return namelist;
1832  }
1833 
1834 
1835 
1845 
1846  void table::prepare_insert_single_row()
1847  {
1848  // call before setting data in individual columns and then calling insert_single_row()
1849  //
1850  //~~~{.cpp}
1851  //prepare_insert_single_row();
1852  //my_table["col1"] = 6;
1853  //my_table["col3"] = "hello";
1854  //insert_single_row();
1855  //~~~
1856 
1857  for (auto& col : fColumns) col.has_data = false;
1858  }
1859 
1860 
1861 
1864 
1865  void table::insert_single_row()
1866  {
1867  // use to add single row data to table. see prepare_insert_single_row() for usage.
1868 
1869  if (database::debug) Info("insert_single_row", "table=%s", name());
1870  TString query = get_insert_command();
1871  int ncols = 0;
1872  for (auto& col : fColumns) {
1873  if (col.has_data) {
1874  if (ncols) query += ",";
1875  query += "\"" + col.Name() + "\"";
1876  ++ncols;
1877  }
1878  }
1879  query += ") VALUES (";
1880  ncols = 0;
1881  for (auto& col : fColumns) {
1882  if (col.has_data) {
1883  if (ncols) query += ",";
1884  query += col.get_data_string_for_insert();
1885  ++ncols;
1886  }
1887  }
1888  query += ")";
1889  if (database::debug) std::cout << query << std::endl;
1890  fDB->execute(query);
1891  }
1892 
1893 
1894  //____________________________________________________________________________//
1895 
1896 }
1897 
ROOT::R::TRInterface & r
#define f(i)
#define c(i)
constexpr Bool_t kFALSE
constexpr Bool_t kTRUE
winID h TVirtualViewer3D TVirtualGLPainter p
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void input
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t result
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t index
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t g
Option_t Option_t width
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h Atom_t Int_t ULong_t ULong_t unsigned char prop_list Atom_t Atom_t Atom_t Time_t type
char name[80]
char * Form(const char *fmt,...)
R__EXTERN TSystem * gSystem
Handles lists of named parameters with different types, a list of KVNamedParameter objects.
TString AsSQLSelection() const
A generic named parameter storing values of different types.
TString GetTString() const
Strings used to represent a set of ranges of values.
Definition: KVNumberList.h:85
Modified copy of TSQLiteServer.
const char * name() const
Definition: SQLiteDB.h:98
const KVNamedParameter & data() const
Definition: SQLiteDB.h:242
TString Name() const
Definition: SQLiteDB.h:102
TString get_table() const
Definition: SQLiteDB.h:115
Interface to ROOT SQLite database backend.
Definition: SQLiteDB.h:473
KVString double_quote_all_tables_and_columns(const TString &) const
Definition: SQLiteDB.cpp:749
const char * name() const
Definition: SQLiteDB.h:329
void primary_key(const TString &cols)
Definition: SQLiteDB.h:428
column & add_column(const KVSQLite::column &c)
Definition: SQLiteDB.cpp:1659
TString get_table_creation_command() const
Definition: SQLiteDB.cpp:1587
bool has_column(const TString &name) const
Definition: SQLiteDB.h:389
Extension of ROOT TString class which allows backwards compatibility with ROOT v3....
Definition: KVString.h:73
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
const char * GetName() const override
virtual const char * GetName() const
virtual const char * GetFieldName(Int_t field)=0
virtual TSQLRow * Next()=0
virtual Int_t GetFieldCount()=0
Ssiz_t Length() const
Int_t Atoi() const
const char * Data() const
void ToUpper()
Bool_t BeginsWith(const char *s, ECaseCompare cmp=kExact) 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]
void Error(const char *location, const char *fmt,...)
void Info(const char *location, const char *fmt,...)
TLine l
ClassImp(TPyArg)