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 
940 
941  bool database::get_next_result() const
942  {
943  // Retrieve next result row resulting from previous call to select_data()
944  // \returns kFALSE when no more data is retrieved
945 
946  if (debug) Info("get_next_result", "now");
947  if (fInserting) {
948  Error("database::get_next_result",
949  "data insertion in progress; call end_data_insertion() then select_data() before this method");
950  return false;
951  }
952  if (!fSelecting) {
953  Error("database::get_next_result",
954  "no data retrieval in progress; select_data() must be called and return true before calling this method");
955  return false;
956  }
957  if (!fEmptyResultSet && fSQLstmt->NextResultRow()) {
958  // set column data
959  int idx = 0;
960  for (auto col : fSQLstmtCols) {
961  col->set_data_from_statement(fSQLstmt.get(), idx);
962  ++idx;
963  }
964  return kTRUE;
965  }
966  fBulkTable = nullptr;
967  fSelecting = false;
968  return kFALSE;
969  }
970 
971 
972 
976 
977  KVNumberList database::get_integer_list(const TString& tables, const TString& column, const TString& selection, const TString& anything_else)
978  {
979  // \note Only for INTEGER columns!
980  // Fill KVNumberList with all `DISTINCT` values of "column" (only 1 column name at a time) for given selection
981 
982  if (column.Contains(",")) {
983  Error("database::get_integer_list", "Only 1 column name at a time please!");
984  return {};
985  }
987  if (select_data(tables, column, selection, true, anything_else)) {
988  auto column_data = fSQLstmtCols.front();
989  while (get_next_result()) {
990  result.Add(column_data->get_data<int>());
991  }
992  }
993  return result;
994  }
995 
996 
997 
1002 
1003  TString database::get_string_list(const TString& tables, const TString& column, const TString& selection, const TString& anything_else)
1004  {
1005  // \note Only for TEXT columns!
1006  // Fill TString with comma-separated list of values of "column" (only 1 column name at a time) for given selection
1007  // \note Any NULL entries will be ignored
1008 
1009  if (column.Contains(",")) {
1010  Error("database::get_string_list", "Only 1 column name at a time please!");
1011  return "";
1012  }
1013  TString result;
1014  if (select_data(tables, column, selection, false, anything_else)) {
1015  auto column_data = fSQLstmtCols.front();
1016  while (get_next_result()) {
1017  if (column_data->is_null()) continue;
1018  if (result != "") result += ",";
1019  result += column_data->get_data<TString>();
1020  }
1021  }
1022  return result;
1023  }
1024 
1025 
1026 
1031 
1032  KVNameValueList database::get_name_value_list(const TString& tablename, const TString& name_column, const TString& value_column, const TString& selection, const TString& anything_else)
1033  {
1034  // Fill KVNameValueList with selected rows from table, adding for each row a parameter with the
1035  // name contained in "name_column" (must be of type `TEXT`) and the value contained in "value_column"
1036  // (can be `INTEGER`, `REAL`, or `TEXT`)
1037 
1039  if (select_data(tablename, Form("%s,%s", name_column.Data(), value_column.Data()), selection, false, anything_else)) {
1040  auto it = fSQLstmtCols.begin();
1041  auto nom = *it++;
1042  auto val = *it;
1043  while (get_next_result()) {
1044  result.SetValue(nom->get_data<TString>(), val->data());
1045  }
1046  }
1047  return result;
1048  }
1049 
1050 
1051 
1055 
1056  TGraph* database::create_graph(const TString& tablename, const TString& Xcolumn, const TString& Ycolumn, const TString& selection)
1057  {
1058  // Create and fill a TGraph from values Xcolumn and Ycolumn in table,
1059  // using the selection if required
1060 
1061  if (select_data(tablename, Form("%s,%s", Xcolumn.Data(), Ycolumn.Data()), selection)) {
1062  TGraph* g = new TGraph;
1063  int i = 0;
1064  auto it = fSQLstmtCols.begin();
1065  auto Xcol = *it++;
1066  auto Ycol = *it;
1067  while (get_next_result()) {
1068  g->SetPoint(i++, Xcol->get_data<double>(), Ycol->get_data<double>());
1069  }
1070  return g;
1071  }
1072  return nullptr;
1073  }
1074 
1075 
1076 
1079 
1080  void database::clear_table(const TString& name)
1081  {
1082  // Delete all rows in table, but does not remove table
1083  delete_rows_in_table(name);
1084  }
1085 
1086 
1087 
1110 
1111  int database::count(const TString& tables, const TString& column, const TString& selection, bool distinct, const TString& anything_else) const
1112  {
1113  // Returns number of rows corresponding to equivalent 'select_data' call
1114  //
1115  // + if column="*" all rows are included
1116  // + if a column name is given, only rows with a non-NULL value for column are counted
1117  // + if distinct=false, count all rows including those with the same value of column
1118  // + if distinct=true, count the number of different values of column
1119  //
1120  // Only 1 column name can be given as second argument
1121  //
1122  // Examples
1123  //
1124  //~~~
1125  // count("some_table")
1126  // => SELECT COUNT(*) FROM some_table;
1127  //
1128  // count("some_table", "some_column")
1129  // => SELECT COUNT(some_column) FROM some_table;
1130  //
1131  // count("some_table", "some_column", "", true)
1132  // => SELECT COUNT(DISTINCT some_column) FROM some_table;
1133  //~~~
1134 
1135  if (debug) Info("count", "tables=%s column=%s selection=%s anything_else=%s",
1136  tables.Data(), column.Data(), selection.Data(), anything_else.Data());
1137  TString qry = "SELECT count(";
1138  if (distinct && column != "*") qry += "DISTINCT ";
1139  if (column != "*") qry += Form("\"%s\"", column.Data());
1140  else qry += "*";
1141  qry += ") FROM ";
1142  KVString table_selection;
1143  generate_table_selection_string(tables, table_selection);
1144  qry += table_selection;
1145  if (selection != "") {
1146  qry += " WHERE " + double_quote_all_tables_and_columns(selection);
1147  }
1148  if (anything_else != "") {
1149  qry += " " + double_quote_all_tables_and_columns(anything_else);
1150  }
1151  if (debug) std::cout << qry << std::endl;
1152  auto result = query(qry);
1153  std::unique_ptr<TSQLRow> row(result->Next());
1154  TString number = row->GetField(0);
1155  return number.Atoi();
1156  }
1157 
1158 
1159 
1168 
1169  bool database::update(const TString& table, const TString& columns, const TString& selection)
1170  {
1171  // update the given columns of an entry in the table corresponding to selection (if given)
1172  // the current values of the data members of the columns will be used
1173  //
1174  // This is equivalent to
1175  //~~~~
1176  // UPDATE [table] SET col1=newval,col2=newval,... [WHERE [selection]]
1177  //~~~~
1178 
1179  if (debug) Info("update", "table=%s columns=%s selection=%s", table.Data(), columns.Data(), selection.Data());
1180  if (fInserting) {
1181  Error("database::update",
1182  "data insertion in progress; call end_data_insertion() before doing anything else");
1183  return false;
1184  }
1185  if (fSelecting) {
1186  Error("database::update",
1187  "data retrieval in progress; call get_next_result() until it returns false before doing anything else");
1188  return false;
1189  }
1190 
1191  fBulkTable = &fTables[table.Data()];
1192  TString query = Form("UPDATE \"%s\" SET ", table.Data());
1193  int ncol = fBulkTable->number_of_columns();
1194  int idx = 0;
1195  for (int i = 0; i < ncol; ++i) {
1196  if (columns.Contains((*fBulkTable)[i].name())) {
1197  if (idx) query += ",";
1198  query += Form("\"%s\"", (*fBulkTable)[i].name());
1199  query += "=?";
1200  ++idx;
1201  }
1202  }
1203  if (selection != "") query += " WHERE " + double_quote_all_tables_and_columns(selection);
1204  if (debug) std::cout << query << std::endl;
1205  if (!prepare_statement(query))
1206  return kFALSE;
1207  fSQLstmt->NextIteration();
1208  idx = 0;
1209  for (int i = 0; i < ncol; ++i) {
1210  if (columns.Contains((*fBulkTable)[i].name())) {
1211  (*fBulkTable)[i].set_data_in_statement(fSQLstmt.get(), idx);
1212  ++idx;
1213  }
1214  }
1215  return (fSQLstmt->Process());
1216  }
1217 
1218 
1219 
1226 
1227  void database::delete_table(const TString& table)
1228  {
1229  // Delete table from database. Equivalent to
1230  //
1231  //~~~~
1232  // DROP TABLE IF EXISTS table
1233  //~~~~
1234  TString query = Form("DROP TABLE IF EXISTS %s", table.Data());
1235  execute(query);
1236  }
1237 
1238 
1239 
1248 
1249  void database::delete_rows_in_table(const TString& table, const TString& selection)
1250  {
1251  // Delete rows from the table corresponding to selection
1252  //
1253  // This is equivalent to
1254  //~~~~
1255  // DELETE FROM [table] WHERE [selection]
1256  //~~~~
1257  // With no selection, deletes all rows of table (clear_table())
1258 
1259  if (debug) Info("delete_data", "table=%s selection=%s",
1260  table.Data(), selection.Data());
1261  TString query = Form("DELETE FROM \"%s\"", table.Data());
1262  if (selection != "") query += " WHERE " + double_quote_all_tables_and_columns(selection);
1263  execute(query);
1264  }
1265 
1266 
1267 
1271 
1272  column& database::add_column(const TString& table, const TString& name, const TString& type)
1273  {
1274  // add column to existing table
1275  // \returns reference to new column
1276  TString query = Form("ALTER TABLE \"%s\" ADD COLUMN \"%s\" %s", table.Data(), name.Data(), type.Data());
1277  execute(query);
1278  return (*this)[table].add_column(name, type);
1279  }
1280 
1281 
1282 
1286 
1287  void database::add_missing_columns(const TString& _table_, const KVNameValueList& l)
1288  {
1289  // add to table any columns which are defined in the list but don't exist
1290  // \note cannot be called during data insertion or retrieval!!!
1291 
1292  if (fInserting) {
1293  Error("database::add_missing_columns",
1294  "data insertion in progress; call end_data_insertion() before doing anything else");
1295  return;
1296  }
1297  if (fSelecting) {
1298  Error("database::add_missing_columns",
1299  "data retrieval in progress; call get_next_result() until it returns false before doing anything else");
1300  return;
1301  }
1302  int ipar = l.GetNpar();
1303  table& tab = (*this)[_table_];
1304  for (int i = 0; i < ipar; ++i) {
1305  KVNamedParameter* par = l.GetParameter(i);
1306  if (!tab.has_column(par->GetName())) add_column(_table_, par->GetName(), par->GetSQLType());
1307  }
1308  }
1309 
1310 
1311 
1319 
1320  void database::copy_table_data(const TString& source, const TString& destination, const TString& columns, const TString& selection)
1321  {
1322  // Copy all selected data in 'source' table to 'destination'
1323  //
1324  // If the columns of the two tables are not identical, specify the columns to copy in 'columns'
1325  // (comma-separated list)
1326  // \note SQLite will not allow copy if the number of selected columns from 'source' is not
1327  // exactly equal to the number of columns in 'destination'
1328 
1329  TString COLUMNS = columns;
1330  if (COLUMNS != "*") {
1331  // quote all column names
1332  COLUMNS = "";
1333  KVString _columns(columns);
1334  _columns.Begin(",");
1335  while (!_columns.End()) {
1336  if (COLUMNS != "") COLUMNS += ", ";
1337  COLUMNS += Form("\"%s\"", _columns.Next(kTRUE).Data());
1338  }
1339  }
1340  TString query = Form("INSERT INTO \"%s\" SELECT %s FROM \"%s\"", destination.Data(), COLUMNS.Data(), source.Data());
1341  if (selection != "") query += " WHERE " + double_quote_all_tables_and_columns(selection);
1342  execute(query);
1343  }
1344 
1345 
1346 
1348 
1349  void column::init_type_map()
1350  {
1351  inv_type_map[KVSQLite::column_type::REAL] = "REAL";
1352  inv_type_map[KVSQLite::column_type::INTEGER] = "INTEGER";
1353  inv_type_map[KVSQLite::column_type::TEXT] = "TEXT";
1354  inv_type_map[KVSQLite::column_type::TIMESTAMP] = "TEXT";
1355  inv_type_map[KVSQLite::column_type::BLOB] = "BLOB";
1356  }
1357 
1358 
1359 
1361 
1362  TString column::_type()
1363  {
1364  return inv_type_map[fNameType.second];
1365  }
1366 
1367 
1368 
1370 
1371  template<> void column::set_data(const KVNamedParameter& x)
1372  {
1373  fData.Set(x.GetName(), x);
1374  fIsNull = false;
1375  has_data = true;
1376  }
1377 
1378 
1379 
1385 
1386  void column::set_data_in_statement(TSQLStatement* s, int idx) const
1387  {
1388  // set value of parameter in SQLite statement corresponding to this column
1389  // \param idx if given, use it as the statement parameter index instead of
1390  // the column's index in the table (case where not all columns are treated
1391  // in the statement)
1392 
1393  if (idx < 0) idx = index();
1394  if (fIsNull) {
1395  // null parameter
1396  s->SetNull(idx);
1397  return;
1398  }
1399  switch (type()) {
1401  s->SetDouble(idx, fData.GetDouble());
1402  break;
1404  s->SetInt(idx, fData.GetInt());
1405  break;
1408  s->SetString(idx, fData.GetString(), -1);
1409  break;
1411  s->SetBinary(idx, fBlob, fBlobSize);
1412  break;
1413  default:
1414  break;
1415  }
1416  }
1417 
1418 
1429 
1430  void column::set_data_from_statement(TSQLStatement* s, int idx) const
1431  {
1432  // set value of column according to value of parameter in statement
1433  //
1434  // any column which has a NULL value will be given value 0, 0.0 or ""
1435  // (for `INTEGER`, `REAL` or `TEXT` type, respectively): use column::is_null()
1436  // to check if this corresponds to a null column value.
1437  //
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  fIsNull = s->IsNull(idx);
1444  switch (type()) {
1446  fData.Set(fIsNull ? 0.0 : s->GetDouble(idx));
1447  break;
1449  fData.Set(fIsNull ? 0 : s->GetInt(idx));
1450  break;
1453  fData.Set(fIsNull ? "" : s->GetString(idx));
1454  break;
1456  if (fIsNull) {
1457  fBlobSize = 0;
1458  }
1459  else {
1460  if (!fBlob) fBlob = (void*) new unsigned char[256];
1461  s->GetBinary(idx, fBlob, fBlobSize);
1462  }
1463  break;
1464  default:
1465  break;
1466  }
1467  }
1468 
1469 
1470 
1475 
1476  TString column::get_data_string_for_insert()
1477  {
1478  // write data in string; for TEXT data we enclose in single quotes
1479  //
1480  // we also replace any "'" with "''" (otherwise SQL error on insert)
1481  if (fData.IsString()) {
1482  auto r = fData.GetTString();
1483  r.ReplaceAll("''", "~#%"); // in case there are already double apostrophes
1484  r.ReplaceAll("'", "''");
1485  r.ReplaceAll("~#%", "''"); // in case there are already double apostrophes
1486  return Form("'%s'", r.Data());
1487  }
1488  return fData.GetTString();
1489  }
1490 
1491 
1492 
1494 
1495  void table::init_type_map()
1496  {
1497  type_map["REAL"] = KVSQLite::column_type::REAL;
1498  type_map["INTEGER"] = KVSQLite::column_type::INTEGER;
1499  type_map["TEXT"] = KVSQLite::column_type::TEXT;
1500  type_map["TIMESTAMP"] = KVSQLite::column_type::TIMESTAMP;
1501  type_map["BLOB"] = KVSQLite::column_type::BLOB;
1502  }
1503 
1504 
1505 
1511 
1512  void table::set_foreign_key_back_references()
1513  {
1514  // called by database::add_table
1515  //
1516  // for any foreign key defined for this table, we set up a back reference in
1517  // the parent table
1518 
1519  for (auto& f : fForeignKeys)
1520  fDB->get_table(f.parent_table).fForeignKeys.push_back({f.parent_key, name(), f.child_key, true});
1521  }
1522 
1523 
1524 
1537 
1538  TString table::get_table_creation_command() const
1539  {
1540  // Create and fill SQLite command to create this table, i.e.
1541  //
1542  //~~~
1543  // CREATE TABLE name (
1544  // col1 TYPE_1 [CONSTRAINT_1]
1545  // col2 TYPE_2 [CONSTRAINT_2]
1546  // ...
1547  // [PRIMARY KEY(col1,...)]
1548  // [FOREIGN KEY(col1,...) REFERENCES other_table(other_column) ...]
1549  // );
1550  //~~~
1551  TString command("CREATE ");
1552  if (is_temporary()) command += "TEMPORARY ";
1553  //command += "TABLE IF NOT EXISTS \"";
1554  command += "TABLE ";
1555  command += "\"" + fName + "\"";
1556  command += " (";
1557  for (int i = 0; i < number_of_columns(); ++i) {
1558  command += "\n ";
1559  command += fColumns[i].get_declaration();
1560  if (i < number_of_columns() - 1) command += ",";
1561  }
1562  if (has_primary_key()) {
1563  command += ",\n PRIMARY KEY(";
1564  command += "\"" + fPrimaryKey + "\"";
1565  command += ")";
1566  }
1567  if (has_foreign_keys()) {
1568  // foreign key constraints
1569  for (auto& fk : fForeignKeys) {
1570  command += ",\n FOREIGN KEY(";
1571  command += "\"" + fk.child_key + "\"";
1572  command += ") REFERENCES ";
1573  command += "\"" + fk.parent_table + "\"";
1574  if (!fk.parent_key.IsNull()) {
1575  command += "(";
1576  command += "\"" + fk.parent_key + "\"";
1577  command += ")";
1578  }
1579  }
1580  }
1581  command += "\n);";
1582  return command;
1583  }
1584 
1585 
1586 
1589 
1590  void table::show_columns() const
1591  {
1592  // print list of columns
1593  std::cout << "Columns in table:" << std::endl;
1594 #ifdef WITH_CPP11
1595  for (auto it = fColumns.begin(); it != fColumns.end(); ++it) {
1596 #else
1597  for (std::vector<KVSQLite::column>::const_iterator it = fColumns.begin(); it != fColumns.end(); ++it) {
1598 #endif
1599  std::cout << "\t" << it->name() << " [" << it->TypeName() << "]" << std::endl;
1600  }
1601  }
1602 
1603 
1604 
1609 
1610  column& table::add_column(const column& c)
1611  {
1612  // add column to table
1613  // \return reference to added column.
1614  // \note cannot be used for existing table in database: see database::add_column()
1615  fColumns.push_back(c);
1616  fColMap[c.name()] = c.index();
1617  fColumns.back().set_table(name());
1618  return fColumns.back();
1619  }
1620 
1621 
1622 
1627 
1628  column& table::add_column(const TString& name, const TString& type)
1629  {
1630  // add column to table
1631  // \return reference to added column
1632  // \note cannot be used for existing table in database: see database::add_column()
1633  return add_column(name, type_map[type]);
1634  }
1635 
1636 
1637 
1660 
1661  void table::foreign_key(const TString& child_key, const TString& parent_table, const TString& parent_key)
1662  {
1663  // add a FOREIGN KEY constraint to the table.
1664  //
1665  // The child key(s) should first be declared using add_column()
1666  //
1667  //~~~
1668  // foreign_key("item_id", "items", "id")
1669  //
1670  // => FOREIGN KEY(item_id) REFERENCES items(id)
1671  //
1672  // foreign_key("item_id", "items")
1673  //
1674  // => FOREIGN KEY(item_id) REFERENCES items
1675  //
1676  // [case where 'items' has an INTEGER PRIMARY KEY, no need to specify]
1677  //
1678  // foreign_key("songartist,songalbum", "album", "albumartist,albumname")
1679  //
1680  // => FOREIGN KEY(songartist, songalbum) REFERENCES album(albumartist, albumname)
1681  //
1682  // [case where both parent and child tables have composite primary keys]
1683  //~~~
1684 
1685  KVString check_columns = child_key;
1686  check_columns.Begin(",");
1687  bool ok = true;
1688  while (!check_columns.End()) {
1689  auto nxt = check_columns.Next(kTRUE);
1690  if (!has_column(nxt)) {
1691  Error("table::foreign_key", "Child key \"%s\" has not being declared with add_column()", nxt.Data());
1692  ok = false;
1693  }
1694  }
1695  if (!ok) return;
1696  fForeignKeys.push_back({child_key, parent_table, parent_key});
1697  }
1698 
1699 
1700 
1704 
1705  int table::check_columns(const KVNameValueList& l)
1706  {
1707  // make sure that all parameters in the list have corresponding columns in the table
1708  // \returns the number of columns to be added
1709 
1710  int ncols = 0;
1711  int ipar = l.GetNpar();
1712  for (int i = 0; i < ipar; ++i) {
1713  KVNamedParameter* par = l.GetParameter(i);
1714  if (!has_column(par->GetName())) ncols++;
1715  }
1716  return ncols;
1717  }
1718 
1719 
1720 
1727 
1728  void table::prepare_data(const KVNameValueList& l, const KVNamedParameter* null_value)
1729  {
1730  // fill all columns in table with data contained in KVNameValueList parameters having the same name.
1731  //
1732  // any columns which do not appear in the KVNameValueList (except for PRIMARY KEY) will be set to 'null'
1733  //
1734  // if required, any parameters with the same type&value as "null_value" will be set to 'null' too
1735 
1736  for (int i = 0; i < number_of_columns(); ++i) {
1737  KVNamedParameter* p = l.FindParameter((*this)[i].name());
1738  if (p && !(null_value && p->HasSameValueAs(*null_value)))
1739  (*this)[i].set_data(*p);
1740  else
1741  (*this)[i].set_null();
1742  }
1743  }
1744 
1745 
1746 
1749 
1750  void table::set_all_columns_null()
1751  {
1752  // set the value of all columns in the table to NULL
1753  for (int i = 0; i < number_of_columns(); ++i) {
1754  (*this)[i].set_null();
1755  }
1756  }
1757 
1758 
1759 
1765 
1766  TString table::get_column_names(const TString& exclude, const TString& delim) const
1767  {
1768  // Return a comma-separated list of the colum names
1769  //
1770  // \param[in] delim separator to use in list (default: ",")
1771  // \param[in] exclude list of column names to exclude from list
1772 
1773  TString namelist;
1774  int added = 0;
1775  for (int i = 0; i < number_of_columns(); ++i) {
1776  TString name = (*this)[i].name();
1777  if (exclude.Contains(name)) continue;
1778  if (added) namelist += delim;
1779  namelist += name;
1780  ++added;
1781  }
1782  return namelist;
1783  }
1784 
1785 
1786 
1796 
1797  void table::prepare_insert_single_row()
1798  {
1799  // call before setting data in individual columns and then calling insert_single_row()
1800  //
1801  //~~~{.cpp}
1802  //prepare_insert_single_row();
1803  //my_table["col1"] = 6;
1804  //my_table["col3"] = "hello";
1805  //insert_single_row();
1806  //~~~
1807 
1808  for (auto& col : fColumns) col.has_data = false;
1809  }
1810 
1811 
1812 
1815 
1816  void table::insert_single_row()
1817  {
1818  // use to add single row data to table. see prepare_insert_single_row() for usage.
1819 
1820  if (database::debug) Info("insert_single_row", "table=%s", name());
1821  TString query = get_insert_command();
1822  int ncols = 0;
1823  for (auto& col : fColumns) {
1824  if (col.has_data) {
1825  if (ncols) query += ",";
1826  query += "\"" + col.Name() + "\"";
1827  ++ncols;
1828  }
1829  }
1830  query += ") VALUES (";
1831  ncols = 0;
1832  for (auto& col : fColumns) {
1833  if (col.has_data) {
1834  if (ncols) query += ",";
1835  query += col.get_data_string_for_insert();
1836  ++ncols;
1837  }
1838  }
1839  query += ")";
1840  if (database::debug) std::cout << query << std::endl;
1841  fDB->execute(query);
1842  }
1843 
1844 
1845  //____________________________________________________________________________//
1846 
1847 }
1848 
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
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:1610
TString get_table_creation_command() const
Definition: SQLiteDB.cpp:1538
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)