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  {
147  if(fDBserv->IsLocked())
148  {
149  // the db file has been locked by another process/thread
150  // sleep, then try again (5 times)
151  int i=2;
152  do
153  {
154  Info("open","Database is locked. Try again in 50ms...");
155  std::this_thread::sleep_for(50ms);
156  Info("open","Try number %d...",i);
157  ++i;
158  }
159  while(!execute("pragma schema_version") && fDBserv->IsLocked() && (i<6));
160  if((i<6) && fDBserv->IsOK())
161  {
162  Info("open","...database successfully opened");
163  fIsValid = true;
164  read_table_infos();
165  return;
166  }
167  }
168  fDBserv->Close();
169  fIsValid = false;
170  return;
171  }
172  fIsValid = true;
173  read_table_infos();
174  }
175 
176 
177 
179 
180  void database::PrintResults(TSQLResult* tabent, int column_width) const
181  {
182  int nfields = tabent->GetFieldCount();
183  std::unique_ptr<TSQLRow> row(nullptr);
184  for (int r = -1; true; ++r) {
185  if (r > -1) {
186  row.reset(tabent->Next());
187  if (row.get() == nullptr) break;
188  std::cout << std::setw(6) << r;
189  }
190  for (int f = 0; f < nfields; ++f) {
191  if (r < 0) {
192  if (f == 0) std::cout << std::setw(6) << "#";
193  std::cout << "|" << std::setw(column_width) << tabent->GetFieldName(f) ;
194  }
195  else {
196  std::cout << "|" << std::setw(column_width) << row->GetField(f) ;
197  }
198  }
199  std::cout << "\n";
200  }
201  }
202 
203 
204 
207 
208  void database::Dump() const
209  {
210  // Print on stdout contents of database
211 
212  std::cout << "Database : " << fDBserv->GetDB() << " [" << fDBserv->GetDBMS() << "]\n";
213  std::unique_ptr<TList> tl(fDBserv->GetTablesList());
214  TObject* o;
215  TIter it_tab(tl.get());
216  while ((o = it_tab())) {
217  std::cout << "\t";
218  std::cout << "Table : " << o->GetName() << "\n";
219  std::unique_ptr<TSQLResult> tabent = SelectRowsFromTable(o->GetName());
220  PrintResults(tabent.get());
221  }
222  std::cout << std::endl;
223  }
224 
225 
228 
229  void database::print_selection(const TString& table, const TString& columns, const TString& condition, int column_width) const
230  {
231  // Print on stdout contents of database
232 
233  std::unique_ptr<TSQLResult> tabent = SelectRowsFromTable(table, columns, condition);
234  PrintResults(tabent.get(), column_width);
235  std::cout << std::endl;
236  }
237 
238 
239 
257 
258  table& database::add_table(const table& t)
259  {
260  // add table to database (if it does not exist already)
261  //
262  // returns reference to new table in database
263  //
264  // _WARNING:_ after calling this method, do not use the table given as argument
265  //
266  // it does not correspond to the table in the database
267  //
268  // instead use `db["table name"]` or the reference returned by this method to access the table
269  //
270  //~~~~{.cpp}
271  // e.g. KVSQLite::table tt("some table");
272  // tt.add_column(...);
273  // db.add_table(tt);
274  // db["some table"]["column"].set_data(...);
275  //~~~~
276 
277  auto command = t.get_table_creation_command();
278  if (debug)
279  std::cout << command << std::endl;
280  if (execute(command)) {
281  fTables.insert(std::pair<std::string, KVSQLite::table>(t.name(), t));
282  (*this)[t.name()].fDB = this;// set pointer to dB in table
283  (*this)[t.name()].set_foreign_key_back_references();
284  }
285  return (*this)[t.name()];
286  }
287 
288 
289 
306 
307  bool database::prepare_data_insertion(const TString& table)
308  {
309  // Call this method before insert_data_row() in order to perform bulk data
310  // insertion operation. Note that this can only be used in cases where values are set for
311  // each column (apart from, eventually, a rowid alias column i.e. an INTEGER PRIMARY KEY)
312  // before insertion. If not, use prepare_data_insertion_selected_columns().
313  //
314  //~~~~{.cpp}
315  // db.prepare_data_insertion("my_table");
316  // while(...){ // loop over data to insert
317  // // set up data in table
318  // db.insert_data_row();
319  // }
320  // db.end_data_insertion(); // terminate data insertion
321  //~~~~
322  // Until method end_data_insertion() is called, you cannot call prepare_data_insertion()
323  // with a different table name.
324 
325  if(debug) Info("prepare_data_insertion","table=%s",table.Data());
326  if (fInserting) {
327  if (fBulkTable) {
328  Error("database::prepare_data_insertion",
329  "bulk insertion in progress for table %s; call database::commit() to terminate transaction",
330  fBulkTable->name());
331  return false;
332  }
333  else {
334  Error("database::prepare_data_insertion",
335  "bulk insertion in progress; call database::commit() to terminate transaction");
336  return false;
337  }
338  }
339  if (fSelecting) {
340  Error("database::prepare_data_insertion",
341  "data retrieval in progress; call get_next_result() until it returns false");
342  return false;
343  }
344  fInserting = true;
345  fDBserv->StartTransaction();
346  // set up SQL statement for data insertion into table
347  fBulkTable = &fTables[table.Data()];
348  TString com(fBulkTable->get_insert_command());
349  int ncol = fBulkTable->number_of_columns();
350  int idx = 0;
351  for (int i = 0; i < ncol; ++i) {
352  if (idx) com += ",";
353  if (!(*fBulkTable)[i].rowid_alias()) {
354  com += Form("\"%s\"", (*fBulkTable)[i].name());;
355  ++idx;
356  }
357  }
358  com += ") VALUES (";
359  idx = 0;
360  for (int i = 0; i < ncol; ++i) {
361  if (idx) com += ",";
362  if (!(*fBulkTable)[i].rowid_alias()) {
363  com += "?";
364  ++idx;
365  }
366  }
367  com += ")";
368  if(debug) std::cout << com << std::endl;
369  return prepare_statement(com);
370  }
371 
372 
373 
398 
399  bool database::prepare_data_insertion_selected_columns(const TString& table)
400  {
401  // Call this method before insert_data_row_selected_columns() in order to perform bulk data
402  // insertion operation without giving values for all columns (apart from an eventual rowid alias column).
403  //
404  //~~~~{.cpp}
405  // // initialise columns
406  // db["my_table"].prepare_insert_single_row();
407  // // set data in columns for first row to insert
408  // db["my_table"]["colA"] = "ok";
409  // db["my_table"]["colC"] = 116;
410  // // set up bulk insertion
411  // db.prepare_data_insertion_selected_columns("my_table");
412  // // insert first row of data
413  // db.insert_data_row();
414  // while(...){ // loop over rest of data to insert
415  // // set up data in table
416  // db["my_table"]["colA"] = "bad";
417  // db["my_table"]["colC"] = -356;
418  // db.insert_data_row();
419  // }
420  // db.end_data_insertion(); // terminate data insertion
421  //~~~~
422  // Until method end_data_insertion() is called, you cannot call prepare_data_insertion()
423  // with a different table name.
424 
425  if(debug) Info("prepare_data_insertion_selected_columns","table=%s",table.Data());
426  if (fInserting) {
427  if (fBulkTable) {
428  Error("database::prepare_data_insertion",
429  "bulk insertion in progress for table %s; call database::commit() to terminate transaction",
430  fBulkTable->name());
431  return false;
432  }
433  else {
434  Error("database::prepare_data_insertion",
435  "bulk insertion in progress; call database::commit() to terminate transaction");
436  return false;
437  }
438  }
439  if (fSelecting) {
440  Error("database::prepare_data_insertion",
441  "data retrieval in progress; call get_next_result() until it returns false");
442  return false;
443  }
444  fInserting = true;
445  fDBserv->StartTransaction();
446  // set up SQL statement for data insertion into table
447  fBulkTable = &fTables[table.Data()];
448  TString com(fBulkTable->get_insert_command());
449  int ncol = fBulkTable->number_of_columns();
450  int idx = 0;
451  for (int i = 0; i < ncol; ++i) {
452  if (idx) com += ",";
453  if (!(*fBulkTable)[i].rowid_alias() && (*fBulkTable)[i].has_data) {
454  com += Form("\"%s\"", (*fBulkTable)[i].name());;
455  ++idx;
456  }
457  }
458  com += ") VALUES (";
459  idx = 0;
460  for (int i = 0; i < ncol; ++i) {
461  if (idx) com += ",";
462  if (!(*fBulkTable)[i].rowid_alias() && (*fBulkTable)[i].has_data) {
463  com += "?";
464  ++idx;
465  }
466  }
467  com += ")";
468  if(debug) std::cout << com << std::endl;
469  return prepare_statement(com);
470  }
471 
472 
473 
475 
476  const char* table::get_insert_command() const
477  {
478  switch (fInsert) {
480  return Form("INSERT OR FAIL INTO \"%s\"(", name());
481  break;
483  return Form("INSERT OR IGNORE INTO \"%s\"(", name());
484  break;
486  return Form("INSERT OR REPLACE INTO \"%s\"(", name());
487  break;
488  default:
490  break;
491  }
492  return Form("INSERT INTO \"%s\"(", name());
493  }
494 
495 
496 
502 
503  TString column::get_declaration() const
504  {
505  // return declaration for column, including type & constraint
506  //
507  // the name of the column is enclosed in double-quotes, in case it is the
508  // same as an SQL keyword
509 
510  TString decl;
511  decl.Form("\"%s\" %s", name(), type_name());
512  if (!fConstraint.IsNull()) {
513  decl += " ";
514  decl += fConstraint;
515  }
516  return decl;
517  }
518 
519 
520 
532 
533  void database::insert_data_row()
534  {
535  // Call (repeatedly) after a call to prepare_data_insertion()
536  // in order to insert current contents of table columns as a new row in the database.
537  //
538  // Value of each column should first be set like this:
539  //
540  //~~~~{.cpp}
541  // db["table"]["id"].set_data(6);
542  // db["table"]["name"] = "triumph";
543  //~~~~
544  // Call end_data_insertion() when all data has been inserted
545 
546  if (!fInserting || !fBulkTable) {
547  Error("database::insert_data_row",
548  "no transaction initialized; call prepare_data_insertion(name_table) before this method");
549  return;
550  }
551  if (fSelecting) {
552  Error("database::insert_data_row",
553  "data retrieval in progress; call get_next_result() until it returns false, then call prepare_data_insertion(name_table) before this method");
554  return;
555  }
556  int ncol = fBulkTable->number_of_columns();
557  fSQLstmt->NextIteration();
558  int idx = 0;
559  for (int i = 0; i < ncol; ++i) {
560  if (!(*fBulkTable)[i].rowid_alias()) {
561  (*fBulkTable)[i].set_data_in_statement(fSQLstmt.get(), idx);
562  ++idx;
563  }
564  }
565  }
566 
567 
579 
580  void database::insert_data_row_selected_columns()
581  {
582  // Call (repeatedly) after a call to prepare_data_insertion()
583  // in order to insert current contents of table columns as a new row in the database.
584  //
585  // Value of each column should first be set like this:
586  //
587  //~~~~{.cpp}
588  // db["table"]["id"].set_data(6);
589  // db["table"]["name"] = "triumph";
590  //~~~~
591  // Call end_data_insertion() when all data has been inserted
592 
593  if (!fInserting || !fBulkTable) {
594  Error("database::insert_data_row",
595  "no transaction initialized; call prepare_data_insertion(name_table) before this method");
596  return;
597  }
598  if (fSelecting) {
599  Error("database::insert_data_row",
600  "data retrieval in progress; call get_next_result() until it returns false, then call prepare_data_insertion(name_table) before this method");
601  return;
602  }
603  int ncol = fBulkTable->number_of_columns();
604  fSQLstmt->NextIteration();
605  int idx = 0;
606  for (int i = 0; i < ncol; ++i) {
607  if (!(*fBulkTable)[i].rowid_alias() && (*fBulkTable)[i].has_data) {
608  (*fBulkTable)[i].set_data_in_statement(fSQLstmt.get(), idx);
609  ++idx;
610  }
611  }
612  }
613 
614 
615 
619 
620  void database::end_data_insertion()
621  {
622  // Call after prepare_data_insertion() & insert_data_row() have been
623  // used to insert data into a table
624 
625  if(debug) Info("end_data_insertion", "now");
626  if (!fInserting) {
627  Error("database::end_data_insertion",
628  "no transaction initialized; call prepare_data_insertion(name_table) first");
629  return;
630  }
631  if (fSelecting) {
632  Error("database::insert_data_row",
633  "data retrieval in progress; call get_next_result() until it returns false, then call prepare_data_insertion(name_table) before this method");
634  return;
635  }
636  fBulkTable = nullptr;
637  fSQLstmt->Process();
638  fDBserv->Commit();
639  fInserting = false;
640  }
641 
642 
645 
646  void database::print_selected_data(const TString& tables, const TString& columns, const TString& selection, bool distinct, const TString& anything_else)
647  {
648  // Print out results of a call to select_data().
649 
650  if (select_data(tables, columns, selection, distinct, anything_else)) {
651  // retrieve column titles (with table name) and store length of each
652  std::vector<TString> col_titles;
653  std::vector<int> col_width;
654  std::for_each(std::begin(fSQLstmtCols), std::end(fSQLstmtCols),
655  [&col_titles, &col_width]
656  (const column * col) {
657  col_titles.push_back(col->get_table() + "::" + col->Name());
658  col_width.push_back(col->get_table().Length() + 2 + col->Name().Length());
659  }
660  );
661  // retrieve column data and update maximum length of column
662  using data_row = std::vector<TString>;
663  std::vector<data_row> col_data;
664  while (get_next_result()) {
665  data_row current_data_row;
666  int col_index = 0;
667  std::for_each(std::begin(fSQLstmtCols), std::end(fSQLstmtCols),
668  [&col_index, &col_width, &current_data_row]
669  (const column * col) {
670  current_data_row.push_back(col->data().GetTString());
671  col_width[col_index] = std::max(current_data_row[col_index].Length(), col_width[col_index]);
672  ++col_index;
673  }
674  );
675  col_data.push_back(current_data_row);
676  }
677  auto print_line = [&col_width]() {
678  for (auto width : col_width) {
679  std::cout << "+-";
680  for (int i = 0; i <= width; ++i) std::cout << "-";
681  }
682  std::cout << "+\n";
683  };
684  auto print_column_item = [](const TString & item, int width, bool end_line = false) {
685  std::cout << "| " << item;
686  for (int i = 0; i <= width - item.Length(); ++i)
687  std::cout << " ";
688  if (end_line)
689  std::cout << "|";
690  };
691  auto print_column_data = [&col_width, &print_column_item](const std::vector<TString>& cols) {
692  int col_index = 0;
693  int ncols = cols.size();
694  for (auto& col : cols) {
695  print_column_item(col, col_width[col_index], (col_index == ncols - 1));
696  ++col_index;
697  }
698  std::cout << std::endl;
699  };
700  print_line();
701  print_column_data(col_titles);
702  print_line();
703  for (auto& row : col_data) print_column_data(row);
704  print_line();
705  }
706  }
707 
708 
709 
711 
712  std::list<const table*> database::generate_table_selection_string(const TString& tables, KVString& table_selection) const
713  {
714  std::list<const table*> table_list;
715  KVString _tables(tables);
716  _tables.Begin(",");
717  // use double-quoted table names in table_selection
718  int i(0);
719  std::unordered_map<std::string, int> table_names;
720  while (!_tables.End()) {
721  TString tabnam = _tables.Next();
722  table_list.push_back(& operator[](tabnam));
723  if (i) {
724  table_selection += " INNER JOIN \"" + tabnam + "\"";
725  // add any foreign key constraints involving any previously mentioned table
726  auto this_table = table_list.back();
727  if (this_table->has_foreign_keys(false)) { // include back references
728  TString on_and = "ON";
729  for (auto& f : this_table->fForeignKeys) {
730  // make sure other table is already in list
731  if (table_names[f.parent_table.Data()]) {
732  table_selection += Form(" %s %s.%s = %s.%s",
733  on_and.Data(),
734  this_table->name(), f.child_key.Data(),
735  f.parent_table.Data(), f.parent_key.Data());
736  on_and = "AND"; // chain successive foreign key constraints: 'ON a=b AND c=d AND ...'
737  }
738  }
739  }
740  }
741  else
742  table_selection += "\"" + tabnam + "\"";
743  ++table_names[tabnam.Data()];
744  ++i;
745  }
746  return table_list;
747  }
748 
749 
750 
752 
754  {
755  auto replacer = [&](const KVString& repl)
756  {
757  KVString replaced;
758  // treat each whitespace-separated token in string separately
759  repl.Begin(" ");
760  while(!repl.End())
761  {
762  auto tok = repl.Next();
763  if(replaced.Length()) replaced+=" ";
764  bool stop_replacing=false;
765  for (auto& tp : fTables)
766  {
767  TString table_name = tp.second.name();
768  if(tok == table_name)
769  {
770  tok.Prepend("\"");
771  tok.Append("\"");
772  stop_replacing=true;
773  break;
774  }
775  for (auto& c : tp.second.fColumns)
776  {
777  TString column_name = c.name();
778  if(tok == column_name)
779  {
780  tok.Prepend("\"");
781  tok.Append("\"");
782  stop_replacing=true;
783  break;
784  }
785  }
786  if(stop_replacing) break;
787  }
788  replaced+=tok;
789  }
790  return replaced;
791  };
792  // no replacements inside string constants i.e. between pairs of ''
793  KVString _input(input);
794  _input.Begin("'");
795  bool in_quotes = _input.BeginsWith("'");
796  KVString output;
797  while(!_input.End())
798  {
799  auto part = _input.Next();
800  if(!in_quotes)
801  {
802  output += replacer(part);
803  }
804  else
805  {
806  output += "'" + part + "'";
807  }
808  in_quotes = !in_quotes;
809  }
810  return output;
811  }
812 
813 
814 
834 
835  bool database::select_data(const TString& tables, const TString& columns, const TString& selection, bool distinct, const TString& anything_else) const
836  {
837  // Select data in database from given table(s) according to
838  //~~~~
839  // SELECT [columns] FROM [tables] WHERE [selection] [anything_else]
840  //~~~~
841  // In order to retrieve results, call get_next_result() until it returns false.
842  //
843  // \param tables if more than 1 table is given, separate table names with commas.
844  // if 1 table has a foreign key referencing the other, this allows to JOIN data
845  // in both tables together. [columns] can then refer to columns in either table.
846  // \param columns ="*" by default, i.e. data from all columns is retrieved.
847  // If specific column data is to be selected, give a comma-separated list of
848  // column names. These will be quoted correctly in case they contain spaces.
849  // \param distinct can be used in conjunction with a selection of specific columns in order
850  // to retrieve only rows of data with different values for the column(s).
851  //
852  // You can use the COUNT() function as one of the column names (upper or lower case).
853  // In this case, the value for each row can be retrieved by calling get_count_column_value()
854  // after each call to get_next_value().
855 
856  if(debug) Info("select_data","tables=%s columns=%s selection=%s anything_else=%s",
857  tables.Data(),columns.Data(),selection.Data(),anything_else.Data());
858  if (fInserting) {
859  Error("database::select_data",
860  "data insertion in progress; call end_data_insertion() before retrieving data");
861  return false;
862  }
863  if (fSelecting) {
864  Error("database::select_data",
865  "data retrieval already in progress; call get_next_result() until it returns false before making new selection");
866  return false;
867  }
868 
869  fSQLstmtCols.clear();
870 
871  KVString table_selection;
872  std::list<const table*> table_list = generate_table_selection_string(tables, table_selection);
873 
874  KVString column_selection(""), _columns(columns);
875  if (columns == "*") {
876  column_selection = "*";
877  distinct = false; // don't allow 'SELECT DISTINCT * FROM ....' (?)
878  // find right column in right table for each item
879  // loop over all columns in each table
880  for (auto tabs : table_list) {
881  for (int i = 0; i < tabs->number_of_columns(); ++i) {
882  fSQLstmtCols.push_back(&(*tabs)[i]);
883  }
884  }
885  }
886  else {
887  if (distinct) column_selection = "DISTINCT ";
888  _columns.Begin(",");
889  int i(0);
890  while (!_columns.End()) {
891  TString colnam = _columns.Next();
892  if (i) column_selection += ", ";
893  ++i;
894  // find right column in right table for this item
895  TString COLNAM = colnam;
896  COLNAM.ToUpper();
897  if (COLNAM.BeginsWith("COUNT(")) { // look for use of COUNT()
898  // no double quotes around COUNT()!
899  column_selection += Form("%s", colnam.Data());
900  fSQLstmtCols.push_back(&count_column);
901  }
902  else {
903  // put double-quoted column names in column_selection
904  column_selection += Form("\"%s\"", colnam.Data());
905  for (auto tabs : table_list)
906  if (tabs->has_column(colnam)) fSQLstmtCols.push_back(&(*tabs)[colnam]);
907  }
908  }
909  }
910 
911  TString cond = Form("SELECT %s FROM %s", column_selection.Data(), table_selection.Data());
912  if (selection != ""){
913  // make sure any column names or table names are double quoted
914  cond += " WHERE " + double_quote_all_tables_and_columns(selection);
915  }
916  if (anything_else != "") {
917  // make sure any column names or table names are double quoted
918  cond += " " + double_quote_all_tables_and_columns(anything_else);
919  }
920  if (debug)
921  std::cout << cond << std::endl;
922  prepare_statement(cond);
923  if (fSQLstmt.get() == nullptr) {
924  Error("database::select_data", "problem processing : %s", cond.Data());
925  fSelecting = false;
926  fBulkTable = nullptr;
927  return false;
928  }
929  fSQLstmt->EnableErrorOutput();
930  if (fSQLstmt->Process()) {
931  fSQLstmt->StoreResult();
932  fSelecting = true;
933  fEmptyResultSet = false;
934  return true;
935  }
936  else if (!fSQLstmt->IsError()) {
937  // query ok, no results correspond to selection
938  fSQLstmt->StoreResult();
939  fSelecting = true;
940  fEmptyResultSet = true;
941  return true;
942  }
943  fSelecting = false;
944  fBulkTable = nullptr;
945  return false;
946  }
947 
948 
949 
953 
954  bool database::get_next_result() const
955  {
956  // Retrieve next result row resulting from previous call to select_data()
957  // \returns kFALSE when no more data is retrieved
958 
959  if(debug) Info("get_next_result","now");
960  if (fInserting) {
961  Error("database::get_next_result",
962  "data insertion in progress; call end_data_insertion() then select_data() before this method");
963  return false;
964  }
965  if (!fSelecting) {
966  Error("database::get_next_result",
967  "no data retrieval in progress; select_data() must be called and return true before calling this method");
968  return false;
969  }
970  if (!fEmptyResultSet && fSQLstmt->NextResultRow()) {
971  // set column data
972  int idx = 0;
973  for (auto col : fSQLstmtCols) {
974  col->set_data_from_statement(fSQLstmt.get(), idx);
975  ++idx;
976  }
977  return kTRUE;
978  }
979  fBulkTable = nullptr;
980  fSelecting = false;
981  return kFALSE;
982  }
983 
984 
985 
989 
990  KVNumberList database::get_integer_list(const TString& tables, const TString& column, const TString& selection, const TString& anything_else)
991  {
992  // \note Only for INTEGER columns!
993  // Fill KVNumberList with all `DISTINCT` values of "column" (only 1 column name at a time) for given selection
994 
995  if(column.Contains(",")){
996  Error("database::get_integer_list", "Only 1 column name at a time please!");
997  return {};
998  }
1000  if (select_data(tables, column, selection, true, anything_else)) {
1001  auto column_data = fSQLstmtCols.front();
1002  while (get_next_result()) {
1003  result.Add(column_data->get_data<int>());
1004  }
1005  }
1006  return result;
1007  }
1008 
1009 
1010 
1015 
1016  TString database::get_string_list(const TString& tables, const TString& column, const TString& selection, const TString& anything_else)
1017  {
1018  // \note Only for TEXT columns!
1019  // Fill TString with comma-separated list of values of "column" (only 1 column name at a time) for given selection
1020  // \note Any NULL entries will be ignored
1021 
1022  if(column.Contains(",")){
1023  Error("database::get_string_list", "Only 1 column name at a time please!");
1024  return "";
1025  }
1026  TString result;
1027  if (select_data(tables, column, selection, false, anything_else)) {
1028  auto column_data = fSQLstmtCols.front();
1029  while (get_next_result()) {
1030  if (column_data->is_null()) continue;
1031  if (result != "") result += ",";
1032  result += column_data->get_data<TString>();
1033  }
1034  }
1035  return result;
1036  }
1037 
1038 
1039 
1044 
1045  KVNameValueList database::get_name_value_list(const TString& tablename, const TString& name_column, const TString& value_column, const TString& selection, const TString& anything_else)
1046  {
1047  // Fill KVNameValueList with selected rows from table, adding for each row a parameter with the
1048  // name contained in "name_column" (must be of type `TEXT`) and the value contained in "value_column"
1049  // (can be `INTEGER`, `REAL`, or `TEXT`)
1050 
1052  if (select_data(tablename, Form("%s,%s", name_column.Data(), value_column.Data()), selection, false, anything_else)) {
1053  auto it = fSQLstmtCols.begin();
1054  auto nom = *it++;
1055  auto val = *it;
1056  while (get_next_result()) {
1057  result.SetValue(nom->get_data<TString>(), val->data());
1058  }
1059  }
1060  return result;
1061  }
1062 
1063 
1064 
1068 
1069  TGraph* database::create_graph(const TString& tablename, const TString& Xcolumn, const TString& Ycolumn, const TString& selection)
1070  {
1071  // Create and fill a TGraph from values Xcolumn and Ycolumn in table,
1072  // using the selection if required
1073 
1074  if (select_data(tablename, Form("%s,%s", Xcolumn.Data(), Ycolumn.Data()), selection)) {
1075  TGraph* g = new TGraph;
1076  int i = 0;
1077  auto it = fSQLstmtCols.begin();
1078  auto Xcol = *it++;
1079  auto Ycol = *it;
1080  while (get_next_result()) {
1081  g->SetPoint(i++, Xcol->get_data<double>(), Ycol->get_data<double>());
1082  }
1083  return g;
1084  }
1085  return nullptr;
1086  }
1087 
1088 
1089 
1092 
1093  void database::clear_table(const TString& name)
1094  {
1095  // Delete all data from table
1096  delete_data(name);
1097  }
1098 
1099 
1100 
1123 
1124  int database::count(const TString& tables, const TString& column, const TString& selection, bool distinct, const TString& anything_else) const
1125  {
1126  // Returns number of rows corresponding to equivalent 'select_data' call
1127  //
1128  // + if column="*" all rows are included
1129  // + if a column name is given, only rows with a non-NULL value for column are counted
1130  // + if distinct=false, count all rows including those with the same value of column
1131  // + if distinct=true, count the number of different values of column
1132  //
1133  // Only 1 column name can be given as second argument
1134  //
1135  // Examples
1136  //
1137  //~~~
1138  // count("some_table")
1139  // => SELECT COUNT(*) FROM some_table;
1140  //
1141  // count("some_table", "some_column")
1142  // => SELECT COUNT(some_column) FROM some_table;
1143  //
1144  // count("some_table", "some_column", "", true)
1145  // => SELECT COUNT(DISTINCT some_column) FROM some_table;
1146  //~~~
1147 
1148  if(debug) Info("count","tables=%s column=%s selection=%s anything_else=%s",
1149  tables.Data(),column.Data(),selection.Data(),anything_else.Data());
1150  TString qry = "SELECT count(";
1151  if(distinct && column!="*") qry += "DISTINCT ";
1152  if(column!="*") qry += Form("\"%s\"", column.Data());
1153  else qry+="*";
1154  qry += ") FROM ";
1155  KVString table_selection;
1156  generate_table_selection_string(tables, table_selection);
1157  qry += table_selection;
1158  if (selection != "") {
1159  qry += " WHERE " + double_quote_all_tables_and_columns(selection);
1160  }
1161  if (anything_else != "") {
1162  qry += " " + double_quote_all_tables_and_columns(anything_else);
1163  }
1164  if(debug) std::cout << qry << std::endl;
1165  auto result = query(qry);
1166  std::unique_ptr<TSQLRow> row(result->Next());
1167  TString number = row->GetField(0);
1168  return number.Atoi();
1169  }
1170 
1171 
1172 
1181 
1182  bool database::update(const TString& table, const TString& columns, const TString& selection)
1183  {
1184  // update the given columns of an entry in the table corresponding to selection (if given)
1185  // the current values of the data members of the columns will be used
1186  //
1187  // This is equivalent to
1188  //~~~~
1189  // UPDATE [table] SET col1=newval,col2=newval,... [WHERE [selection]]
1190  //~~~~
1191 
1192  if(debug) Info("update","table=%s columns=%s selection=%s",table.Data(),columns.Data(),selection.Data());
1193  if (fInserting) {
1194  Error("database::update",
1195  "data insertion in progress; call end_data_insertion() before doing anything else");
1196  return false;
1197  }
1198  if (fSelecting) {
1199  Error("database::update",
1200  "data retrieval in progress; call get_next_result() until it returns false before doing anything else");
1201  return false;
1202  }
1203 
1204  fBulkTable = &fTables[table.Data()];
1205  TString query = Form("UPDATE \"%s\" SET ", table.Data());
1206  int ncol = fBulkTable->number_of_columns();
1207  int idx = 0;
1208  for (int i = 0; i < ncol; ++i) {
1209  if (columns.Contains((*fBulkTable)[i].name())) {
1210  if (idx) query += ",";
1211  query += Form("\"%s\"", (*fBulkTable)[i].name());
1212  query += "=?";
1213  ++idx;
1214  }
1215  }
1216  if (selection != "") query += " WHERE " + double_quote_all_tables_and_columns(selection);
1217  if(debug) std::cout << query << std::endl;
1218  if(!prepare_statement(query))
1219  return kFALSE;
1220  fSQLstmt->NextIteration();
1221  idx = 0;
1222  for (int i = 0; i < ncol; ++i) {
1223  if (columns.Contains((*fBulkTable)[i].name())) {
1224  (*fBulkTable)[i].set_data_in_statement(fSQLstmt.get(), idx);
1225  ++idx;
1226  }
1227  }
1228  return (fSQLstmt->Process());
1229  }
1230 
1231 
1232 
1239 
1240  void database::delete_table(const TString& table)
1241  {
1242  // Delete table from database. Equivalent to
1243  //
1244  //~~~~
1245  // DROP TABLE IF EXISTS table
1246  //~~~~
1247  TString query = Form("DROP TABLE IF EXISTS %s", table.Data());
1248  execute(query);
1249  }
1250 
1251 
1252 
1261 
1262  void database::delete_data(const TString& table, const TString& selection)
1263  {
1264  // delete rows from the table corresponding to selection
1265  //
1266  // This is equivalent to
1267  //~~~~
1268  // DELETE FROM [table] WHERE [selection]
1269  //~~~~
1270  // With no selection, deletes all rows of table (clear_table())
1271 
1272  if(debug) Info("delete_data","table=%s selection=%s",
1273  table.Data(),selection.Data());
1274  TString query = Form("DELETE FROM \"%s\"", table.Data());
1275  if (selection != "") query += " WHERE " + double_quote_all_tables_and_columns(selection);
1276  execute(query);
1277  }
1278 
1279 
1280 
1284 
1285  column& database::add_column(const TString& table, const TString& name, const TString& type)
1286  {
1287  // add column to existing table
1288  // \returns reference to new column
1289  TString query = Form("ALTER TABLE \"%s\" ADD COLUMN \"%s\" %s", table.Data(), name.Data(), type.Data());
1290  execute(query);
1291  return (*this)[table].add_column(name, type);
1292  }
1293 
1294 
1295 
1299 
1300  void database::add_missing_columns(const TString& _table_, const KVNameValueList& l)
1301  {
1302  // add to table any columns which are defined in the list but don't exist
1303  // \note cannot be called during data insertion or retrieval!!!
1304 
1305  if (fInserting) {
1306  Error("database::add_missing_columns",
1307  "data insertion in progress; call end_data_insertion() before doing anything else");
1308  return;
1309  }
1310  if (fSelecting) {
1311  Error("database::add_missing_columns",
1312  "data retrieval in progress; call get_next_result() until it returns false before doing anything else");
1313  return;
1314  }
1315  int ipar = l.GetNpar();
1316  table& tab = (*this)[_table_];
1317  for (int i = 0; i < ipar; ++i) {
1318  KVNamedParameter* par = l.GetParameter(i);
1319  if (!tab.has_column(par->GetName())) add_column(_table_, par->GetName(), par->GetSQLType());
1320  }
1321  }
1322 
1323 
1324 
1332 
1333  void database::copy_table_data(const TString& source, const TString& destination, const TString& columns, const TString& selection)
1334  {
1335  // Copy all selected data in 'source' table to 'destination'
1336  //
1337  // If the columns of the two tables are not identical, specify the columns to copy in 'columns'
1338  // (comma-separated list)
1339  // \note SQLite will not allow copy if the number of selected columns from 'source' is not
1340  // exactly equal to the number of columns in 'destination'
1341 
1342  TString COLUMNS = columns;
1343  if (COLUMNS != "*") {
1344  // quote all column names
1345  COLUMNS = "";
1346  KVString _columns(columns);
1347  _columns.Begin(",");
1348  while (!_columns.End()) {
1349  if (COLUMNS != "") COLUMNS += ", ";
1350  COLUMNS += Form("\"%s\"", _columns.Next(kTRUE).Data());
1351  }
1352  }
1353  TString query = Form("INSERT INTO \"%s\" SELECT %s FROM \"%s\"", destination.Data(), COLUMNS.Data(), source.Data());
1354  if (selection != "") query += " WHERE " + double_quote_all_tables_and_columns(selection);
1355  execute(query);
1356  }
1357 
1358 
1359 
1361 
1362  void column::init_type_map()
1363  {
1364  inv_type_map[KVSQLite::column_type::REAL] = "REAL";
1365  inv_type_map[KVSQLite::column_type::INTEGER] = "INTEGER";
1366  inv_type_map[KVSQLite::column_type::TEXT] = "TEXT";
1367  inv_type_map[KVSQLite::column_type::TIMESTAMP] = "TEXT";
1368  inv_type_map[KVSQLite::column_type::BLOB] = "BLOB";
1369  }
1370 
1371 
1372 
1374 
1375  TString column::_type()
1376  {
1377  return inv_type_map[fNameType.second];
1378  }
1379 
1380 
1381 
1383 
1384  template<> void column::set_data(const KVNamedParameter& x)
1385  {
1386  fData.Set(x.GetName(), x);
1387  fIsNull = false;
1388  has_data = true;
1389  }
1390 
1391 
1392 
1398 
1399  void column::set_data_in_statement(TSQLStatement* s, int idx) const
1400  {
1401  // set value of parameter in SQLite statement corresponding to this column
1402  // \param idx if given, use it as the statement parameter index instead of
1403  // the column's index in the table (case where not all columns are treated
1404  // in the statement)
1405 
1406  if (idx < 0) idx = index();
1407  if (fIsNull) {
1408  // null parameter
1409  s->SetNull(idx);
1410  return;
1411  }
1412  switch (type()) {
1414  s->SetDouble(idx, fData.GetDouble());
1415  break;
1417  s->SetInt(idx, fData.GetInt());
1418  break;
1421  s->SetString(idx, fData.GetString(), -1);
1422  break;
1424  s->SetBinary(idx, fBlob, fBlobSize);
1425  break;
1426  default:
1427  break;
1428  }
1429  }
1430 
1431 
1442 
1443  void column::set_data_from_statement(TSQLStatement* s, int idx) const
1444  {
1445  // set value of column according to value of parameter in statement
1446  //
1447  // any column which has a NULL value will be given value 0, 0.0 or ""
1448  // (for `INTEGER`, `REAL` or `TEXT` type, respectively): use column::is_null()
1449  // to check if this corresponds to a null column value.
1450  //
1451  // \param idx if given, use it as the statement parameter index instead of
1452  // the column's index in the table (case where not all columns are treated
1453  // in the statement)
1454 
1455  if (idx < 0) idx = index();
1456  fIsNull = s->IsNull(idx);
1457  switch (type()) {
1459  fData.Set(fIsNull ? 0.0 : s->GetDouble(idx));
1460  break;
1462  fData.Set(fIsNull ? 0 : s->GetInt(idx));
1463  break;
1466  fData.Set(fIsNull ? "" : s->GetString(idx));
1467  break;
1469  if (fIsNull) {
1470  fBlobSize = 0;
1471  }
1472  else {
1473  if (!fBlob) fBlob = (void*) new unsigned char[256];
1474  s->GetBinary(idx, fBlob, fBlobSize);
1475  }
1476  break;
1477  default:
1478  break;
1479  }
1480  }
1481 
1482 
1483 
1488 
1489  TString column::get_data_string_for_insert()
1490  {
1491  // write data in string; for TEXT data we enclose in single quotes
1492  //
1493  // we also replace any "'" with "''" (otherwise SQL error on insert)
1494  if (fData.IsString()) {
1495  auto r = fData.GetTString();
1496  r.ReplaceAll("''", "~#%"); // in case there are already double apostrophes
1497  r.ReplaceAll("'", "''");
1498  r.ReplaceAll("~#%", "''"); // in case there are already double apostrophes
1499  return Form("'%s'", r.Data());
1500  }
1501  return fData.GetTString();
1502  }
1503 
1504 
1505 
1507 
1508  void table::init_type_map()
1509  {
1510  type_map["REAL"] = KVSQLite::column_type::REAL;
1511  type_map["INTEGER"] = KVSQLite::column_type::INTEGER;
1512  type_map["TEXT"] = KVSQLite::column_type::TEXT;
1513  type_map["TIMESTAMP"] = KVSQLite::column_type::TIMESTAMP;
1514  type_map["BLOB"] = KVSQLite::column_type::BLOB;
1515  }
1516 
1517 
1518 
1524 
1525  void table::set_foreign_key_back_references()
1526  {
1527  // called by database::add_table
1528  //
1529  // for any foreign key defined for this table, we set up a back reference in
1530  // the parent table
1531 
1532  for (auto& f : fForeignKeys)
1533  fDB->get_table(f.parent_table).fForeignKeys.push_back({f.parent_key, name(), f.child_key, true});
1534  }
1535 
1536 
1537 
1550 
1551  TString table::get_table_creation_command() const
1552  {
1553  // Create and fill SQLite command to create this table, i.e.
1554  //
1555  //~~~
1556  // CREATE TABLE name (
1557  // col1 TYPE_1 [CONSTRAINT_1]
1558  // col2 TYPE_2 [CONSTRAINT_2]
1559  // ...
1560  // [PRIMARY KEY(col1,...)]
1561  // [FOREIGN KEY(col1,...) REFERENCES other_table(other_column) ...]
1562  // );
1563  //~~~
1564  TString command("CREATE ");
1565  if (is_temporary()) command += "TEMPORARY ";
1566  //command += "TABLE IF NOT EXISTS \"";
1567  command += "TABLE ";
1568  command += "\"" + fName + "\"";
1569  command += " (";
1570  for (int i = 0; i < number_of_columns(); ++i) {
1571  command += "\n ";
1572  command += fColumns[i].get_declaration();
1573  if (i < number_of_columns() - 1) command += ",";
1574  }
1575  if (has_primary_key()) {
1576  command += ",\n PRIMARY KEY(";
1577  command += "\"" + fPrimaryKey + "\"";
1578  command += ")";
1579  }
1580  if (has_foreign_keys()) {
1581  // foreign key constraints
1582  for (auto& fk : fForeignKeys) {
1583  command += ",\n FOREIGN KEY(";
1584  command += "\"" + fk.child_key + "\"";
1585  command += ") REFERENCES ";
1586  command += "\"" + fk.parent_table + "\"";
1587  if (!fk.parent_key.IsNull()) {
1588  command += "(";
1589  command += "\"" + fk.parent_key + "\"";
1590  command += ")";
1591  }
1592  }
1593  }
1594  command += "\n);";
1595  return command;
1596  }
1597 
1598 
1599 
1602 
1603  void table::show_columns() const
1604  {
1605  // print list of columns
1606  std::cout << "Columns in table:" << std::endl;
1607 #ifdef WITH_CPP11
1608  for (auto it = fColumns.begin(); it != fColumns.end(); ++it) {
1609 #else
1610  for (std::vector<KVSQLite::column>::const_iterator it = fColumns.begin(); it != fColumns.end(); ++it) {
1611 #endif
1612  std::cout << "\t" << it->name() << " [" << it->type_name() << "]" << std::endl;
1613  }
1614  }
1615 
1616 
1617 
1622 
1623  column& table::add_column(const column& c)
1624  {
1625  // add column to table
1626  // \return reference to added column.
1627  // \note cannot be used for existing table in database: see database::add_column()
1628  fColumns.push_back(c);
1629  fColMap[c.name()] = c.index();
1630  fColumns.back().set_table(name());
1631  return fColumns.back();
1632  }
1633 
1634 
1635 
1640 
1641  column& table::add_column(const TString& name, const TString& type)
1642  {
1643  // add column to table
1644  // \return reference to added column
1645  // \note cannot be used for existing table in database: see database::add_column()
1646  return add_column(name, type_map[type]);
1647  }
1648 
1649 
1650 
1673 
1674  void table::foreign_key(const TString& child_key, const TString& parent_table, const TString& parent_key)
1675  {
1676  // add a FOREIGN KEY constraint to the table.
1677  //
1678  // The child key(s) should first be declared using add_column()
1679  //
1680  //~~~
1681  // foreign_key("item_id", "items", "id")
1682  //
1683  // => FOREIGN KEY(item_id) REFERENCES items(id)
1684  //
1685  // foreign_key("item_id", "items")
1686  //
1687  // => FOREIGN KEY(item_id) REFERENCES items
1688  //
1689  // [case where 'items' has an INTEGER PRIMARY KEY, no need to specify]
1690  //
1691  // foreign_key("songartist,songalbum", "album", "albumartist,albumname")
1692  //
1693  // => FOREIGN KEY(songartist, songalbum) REFERENCES album(albumartist, albumname)
1694  //
1695  // [case where both parent and child tables have composite primary keys]
1696  //~~~
1697 
1698  KVString check_columns = child_key;
1699  check_columns.Begin(",");
1700  bool ok = true;
1701  while (!check_columns.End()) {
1702  auto nxt = check_columns.Next(kTRUE);
1703  if (!has_column(nxt)) {
1704  Error("table::foreign_key", "Child key \"%s\" has not being declared with add_column()", nxt.Data());
1705  ok = false;
1706  }
1707  }
1708  if (!ok) return;
1709  fForeignKeys.push_back({child_key, parent_table, parent_key});
1710  }
1711 
1712 
1713 
1717 
1718  int table::check_columns(const KVNameValueList& l)
1719  {
1720  // make sure that all parameters in the list have corresponding columns in the table
1721  // \returns the number of columns to be added
1722 
1723  int ncols = 0;
1724  int ipar = l.GetNpar();
1725  for (int i = 0; i < ipar; ++i) {
1726  KVNamedParameter* par = l.GetParameter(i);
1727  if (!has_column(par->GetName())) ncols++;
1728  }
1729  return ncols;
1730  }
1731 
1732 
1733 
1740 
1741  void table::prepare_data(const KVNameValueList& l, const KVNamedParameter* null_value)
1742  {
1743  // fill all columns in table with data contained in KVNameValueList parameters having the same name.
1744  //
1745  // any columns which do not appear in the KVNameValueList (except for PRIMARY KEY) will be set to 'null'
1746  //
1747  // if required, any parameters with the same type&value as "null_value" will be set to 'null' too
1748 
1749  for (int i = 0; i < number_of_columns(); ++i) {
1750  KVNamedParameter* p = l.FindParameter((*this)[i].name());
1751  if (p && !(null_value && p->HasSameValueAs(*null_value)))
1752  (*this)[i].set_data(*p);
1753  else
1754  (*this)[i].set_null();
1755  }
1756  }
1757 
1758 
1759 
1762 
1763  void table::set_all_columns_null()
1764  {
1765  // set the value of all columns in the table to NULL
1766  for (int i = 0; i < number_of_columns(); ++i) {
1767  (*this)[i].set_null();
1768  }
1769  }
1770 
1771 
1772 
1778 
1779  TString table::get_column_names(const TString& exclude, const TString& delim) const
1780  {
1781  // Return a comma-separated list of the colum names
1782  //
1783  // \param[in] delim separator to use in list (default: ",")
1784  // \param[in] exclude list of column names to exclude from list
1785 
1786  TString namelist;
1787  int added = 0;
1788  for (int i = 0; i < number_of_columns(); ++i) {
1789  TString name = (*this)[i].name();
1790  if (exclude.Contains(name)) continue;
1791  if (added) namelist += delim;
1792  namelist += name;
1793  ++added;
1794  }
1795  return namelist;
1796  }
1797 
1798 
1799 
1809 
1810  void table::prepare_insert_single_row()
1811  {
1812  // call before setting data in individual columns and then calling insert_single_row()
1813  //
1814  //~~~{.cpp}
1815  //prepare_insert_single_row();
1816  //my_table["col1"] = 6;
1817  //my_table["col3"] = "hello";
1818  //insert_single_row();
1819  //~~~
1820 
1821  for (auto& col : fColumns) col.has_data = false;
1822  }
1823 
1824 
1825 
1828 
1829  void table::insert_single_row()
1830  {
1831  // use to add single row data to table. see prepare_insert_single_row() for usage.
1832 
1833  if(database::debug) Info("insert_single_row","table=%s",name());
1834  TString query = get_insert_command();
1835  int ncols = 0;
1836  for (auto& col : fColumns) {
1837  if (col.has_data) {
1838  if (ncols) query += ",";
1839  query += "\"" + col.Name() + "\"";
1840  ++ncols;
1841  }
1842  }
1843  query += ") VALUES (";
1844  ncols = 0;
1845  for (auto& col : fColumns) {
1846  if (col.has_data) {
1847  if (ncols) query += ",";
1848  query += col.get_data_string_for_insert();
1849  ++ncols;
1850  }
1851  }
1852  query += ")";
1853  if(database::debug) std::cout << query << std::endl;
1854  fDB->execute(query);
1855  }
1856 
1857 
1858  //____________________________________________________________________________//
1859 
1860 }
1861 
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.
A generic named parameter storing values of different types.
const Char_t * GetSQLType() const
TString GetTString() const
Strings used to represent a set of ranges of values.
Definition: KVNumberList.h:85
const char * name() const
Definition: SQLiteDB.h:98
const KVNamedParameter & data() const
Definition: SQLiteDB.h:246
TString Name() const
Definition: SQLiteDB.h:102
TString get_table() const
Definition: SQLiteDB.h:119
Interface to ROOT SQLite database backend.
Definition: SQLiteDB.h:477
KVString double_quote_all_tables_and_columns(const TString &) const
Definition: SQLiteDB.cpp:753
const char * name() const
Definition: SQLiteDB.h:333
void primary_key(const TString &cols)
Definition: SQLiteDB.h:432
column & add_column(const KVSQLite::column &c)
Definition: SQLiteDB.cpp:1623
TString get_table_creation_command() const
Definition: SQLiteDB.cpp:1551
bool has_column(const TString &name) const
Definition: SQLiteDB.h:393
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)