KaliVeda  1.12/06
Heavy-Ion Analysis Toolkit
SQLiteDB.cpp
Go to the documentation of this file.
1 //Created by KVClassFactory on Mon Jul 25 15:33:56 2016
2 //Author: John Frankland,,,
3 
4 #include "SQLiteDB.h"
5 #include <TList.h>
6 #include "TSQLResult.h"
7 #include "TSQLRow.h"
8 #include "TSQLTableInfo.h"
9 #include "TSQLColumnInfo.h"
10 #include <KVString.h>
11 #include <iostream>
12 #include "KVError.h"
13 #include <iomanip>
14 
18 
19 
20 namespace KVSQLite {
21 
22  // static maps instantiation
23  std::map<TString, KVSQLite::column_type::types> table::type_map;
24  std::map<KVSQLite::column_type::types, TString> column::inv_type_map;
25 
26 
32 
33  unique_ptr<TSQLResult> database::SelectRowsFromTable(const TString& table, const TString& columns, const TString& condition) const
34  {
35  // \param[in] table name of table
36  // \param[in] columns comma-separated list of columns
37  // \param[in] condition selection to be applied, if any
38  // \returns result of query `SELECT [columns] FROM [table] WHERE [condition]`
39 
40  TString query;
41  query.Form("SELECT %s FROM '%s'", columns.Data(), table.Data());
42  if (condition != "") query += Form(" WHERE %s", condition.Data());
43  return unique_ptr<TSQLResult>(fDBserv->Query(query));
44  }
45 
46 
47 
50 
51  void database::read_table_infos()
52  {
53  // initialise map of database tables from existing database
54  unique_ptr<TList> tl(fDBserv->GetTablesList());
55  TObject* o;
56  TIter it_tab(tl.get());
57  while ((o = it_tab())) {
58 
59  table t(o->GetName());
60 
61  unique_ptr<TSQLTableInfo> ti(fDBserv->GetTableInfo(o->GetName()));
62 
63  TIter it_col(ti->GetColumns());
64  TSQLColumnInfo* colin;
65  while ((colin = (TSQLColumnInfo*)it_col())) t.add_column(colin->GetName(), colin->GetTypeName());
66 
67  fTables.insert(std::pair<std::string, KVSQLite::table>(o->GetName(), t));
68  }
69 
70  }
71 
72 
73 
76 
77  void database::show_tables() const
78  {
79  // print list of tables
80  std::cout << "Tables in database:" << std::endl;
81 #ifdef WITH_CPP11
82  for (auto it = fTables.begin();
83 #else
84  for (std::map<std::string, KVSQLite::table>::const_iterator it = fTables.begin();
85 #endif
86  it != fTables.end(); ++it) {
87  std::cout << "\t" << it->first << std::endl;
88  }
89  }
90 
91 
92 
96 
97  void database::open(const TString& dbfile)
98  {
99  // Open/create sqlite db file given path
100  // \param[in] dbfile full path to file
101  TString uri = "sqlite://" + dbfile;
102  fDBserv.reset(static_cast<TSQLiteServer*>(TSQLServer::Connect(uri, 0, 0)));
103  // check for valid database file
104  if (!fDBserv->Exec("pragma schema_version")) {
105  fDBserv->Close();
106  fIsValid = false;
107  return;
108  }
109  fIsValid = true;
110  read_table_infos();
111  }
112 
113 
114 
116 
117  void database::PrintResults(TSQLResult* tabent, int column_width) const
118  {
119  int nfields = tabent->GetFieldCount();
120  unique_ptr<TSQLRow> row(nullptr);
121  for (int r = -1; true; ++r) {
122  if (r > -1) {
123  row.reset(tabent->Next());
124  if (row.get() == nullptr) break;
125  std::cout << std::setw(6) << r;
126  }
127  for (int f = 0; f < nfields; ++f) {
128  if (r < 0) {
129  if (f == 0) std::cout << std::setw(6) << "#";
130  std::cout << "|" << std::setw(column_width) << tabent->GetFieldName(f) ;
131  }
132  else {
133  std::cout << "|" << std::setw(column_width) << row->GetField(f) ;
134  }
135  }
136  std::cout << "\n";
137  }
138  }
139 
140 
141 
144 
145  void database::Dump() const
146  {
147  // Print on stdout contents of database
148 
149  std::cout << "Database : " << fDBserv->GetDB() << " [" << fDBserv->GetDBMS() << "]\n";
150  unique_ptr<TList> tl(fDBserv->GetTablesList());
151  TObject* o;
152  TIter it_tab(tl.get());
153  while ((o = it_tab())) {
154  std::cout << "\t";
155  std::cout << "Table : " << o->GetName() << "\n";
156  unique_ptr<TSQLResult> tabent = SelectRowsFromTable(o->GetName());
157  PrintResults(tabent.get());
158  }
159  std::cout << std::endl;
160  }
161 
162 
165 
166  void database::print_selection(const TString& table, const TString& columns, const TString& condition, int column_width) const
167  {
168  // Print on stdout contents of database
169 
170  unique_ptr<TSQLResult> tabent = SelectRowsFromTable(table, columns, condition);
171  PrintResults(tabent.get(), column_width);
172  std::cout << std::endl;
173  }
174 
175 
176 
192 
193  void database::add_table(table& t)
194  {
195  // add table to database (if it does not exist already)
196  //
197  // _WARNING:_ after calling this method, do not use the table given as argument
198  //
199  // it does not correspond to the table in the database
200  //
201  // instead use `db["table name"]` to access the table
202  //
203  //~~~~{.cpp}
204  // e.g. KVSQLite::table tt("some table");
205  // tt.add_column(...);
206  // db.add_table(tt);
207  // db["some table"]["column"].set_data(...);
208  //~~~~
209 
210  TString command("CREATE ");
211  if (t.is_temporary()) command += "TEMPORARY ";
212  command += "TABLE IF NOT EXISTS \"";
213  command += t.name();
214  command += "\"";
215  command += " (";
216  for (int i = 0; i < t.number_of_columns(); ++i) {
217  if (i) command += ", ";
218  command += t[i].get_declaration();
219  }
220  command += ")";
221  //std::cout << command << std::endl;
222  if (fDBserv->Exec(command))
223  fTables.insert(std::pair<std::string, KVSQLite::table>(t.name(), t));
224  }
225 
226 
227 
242 
243  bool database::prepare_data_insertion(const TString& table)
244  {
245  // Call this method before insert_dat_row() in order to perform bulk data
246  // insertion operation. i.e. something like:
247  //
248  //~~~~{.cpp}
249  // db.prepare_data_insertion("my_table");
250  // while(...){ // loop over data to insert
251  // // set up data in table
252  // db.insert_data_row();
253  // }
254  // db.end_data_insertion(); // terminate data insertion
255  //~~~~
256  // Until method end_data_insertion() is called, you cannot call prepare_data_insertion()
257  // with a different table name.
258 
259  if (fInserting) {
260  if (fBulkTable) {
261  Error("database::prepare_data_insertion",
262  "bulk insertion in progress for table %s; call database::commit() to terminate transaction",
263  fBulkTable->name());
264  return false;
265  }
266  else {
267  Error("database::prepare_data_insertion",
268  "bulk insertion in progress; call database::commit() to terminate transaction");
269  return false;
270  }
271  }
272  if (fSelecting) {
273  Error("database::prepare_data_insertion",
274  "data retrieval in progress; call get_next_result() until it returns false");
275  return false;
276  }
277  fInserting = true;
278  fDBserv->StartTransaction();
279  // set up SQL statement for data insertion into table
280  fBulkTable = &fTables[table.Data()];
281  TString com(fBulkTable->get_insert_command());
282  int ncol = fBulkTable->number_of_columns();
283  int idx = 0;
284  for (int i = 0; i < ncol; ++i) {
285  if (idx) com += ",";
286  if (!(*fBulkTable)[i].primary_key()) {
287  com += Form("\"%s\"", (*fBulkTable)[i].name());;
288  ++idx;
289  }
290  }
291  com += ") VALUES (";
292  idx = 0;
293  for (int i = 0; i < ncol; ++i) {
294  if (idx) com += ",";
295  if (!(*fBulkTable)[i].primary_key()) {
296  com += "?";
297  ++idx;
298  }
299  }
300  com += ")";
301  //std::cout << com << std::endl;
302  fSQLstmt.reset(fDBserv->Statement(com));
303  return true;
304  }
305 
306 
307 
309 
310  const char* table::get_insert_command() const
311  {
312  switch (fInsert) {
314  return Form("INSERT OR FAIL INTO \"%s\"(", name());
315  break;
317  return Form("INSERT OR IGNORE INTO \"%s\"(", name());
318  break;
320  return Form("INSERT OR REPLACE INTO \"%s\"(", name());
321  break;
322  default:
324  break;
325  }
326  return Form("INSERT INTO \"%s\"(", name());
327  }
328 
329 
330 
333 
334  const char* column::get_declaration() const
335  {
336  // return declaration for column, including type & constraint
337 
338  static TString decl;
339  decl.Form("\"%s\" %s", name(), type_name());
340  if (fForeignKey) {
341  decl += " REFERENCES ";
342  decl += Form("\"%s\"(\"%s\")", fFKtable.Data(), fFKcolumn.Data());
343  }
344  else {
345  decl += " ";
346  decl += fConstraint;
347  }
348  return decl.Data();
349  }
350 
351 
352 
364 
365  void database::insert_data_row()
366  {
367  // Call (repeatedly) after a call to prepare_data_insertion()
368  // in order to insert current contents of table columns as a new row in the database.
369  //
370  // Value of each column should first be set like this:
371  //
372  //~~~~{.cpp}
373  // db["table"]["id"].set_data(6);
374  // db["table"]["name"].set_data("triumph");
375  //~~~~
376  // Call end_data_insertion() when all data has been inserted
377 
378  if (!fInserting || !fBulkTable) {
379  Error("database::insert_data_row",
380  "no transaction initialized; call prepare_data_insertion(name_table) before this method");
381  return;
382  }
383  if (fSelecting) {
384  Error("database::insert_data_row",
385  "data retrieval in progress; call get_next_result() until it returns false, then call prepare_data_insertion(name_table) before this method");
386  return;
387  }
388  int ncol = fBulkTable->number_of_columns();
389  fSQLstmt->NextIteration();
390  int idx = 0;
391  for (int i = 0; i < ncol; ++i) {
392  if (!(*fBulkTable)[i].primary_key()) {
393  (*fBulkTable)[i].set_data_in_statement(fSQLstmt.get(), idx);
394  ++idx;
395  }
396  }
397  }
398 
399 
400 
404 
405  void database::end_data_insertion()
406  {
407  // Call after prepare_data_insertion() & insert_data_row() have been
408  // used to insert data into a table
409 
410  if (!fInserting) {
411  Error("database::end_data_insertion",
412  "no transaction initialized; call prepare_data_insertion(name_table) first");
413  return;
414  }
415  if (fSelecting) {
416  Error("database::insert_data_row",
417  "data retrieval in progress; call get_next_result() until it returns false, then call prepare_data_insertion(name_table) before this method");
418  return;
419  }
420  fBulkTable = nullptr;
421  fSQLstmt->Process();
422  fDBserv->Commit();
423  fInserting = false;
424  }
425 
426 
427 
440 
441  bool database::select_data(const TString& table, const TString& columns, const TString& selection, bool distinct, const TString& anything_else) const
442  {
443  // Select data in database from given table according to
444  //~~~~
445  // SELECT [columns] FROM [table] WHERE [selection] [anything_else]
446  //~~~~
447  // In order to retrieve results, call get_next_result() until it returns false.
448  //
449  // \param columns ="*" by default, i.e. data from all columns is retrieved.
450  // If specific column data is to be selected, give a comma-separated list of
451  // column names. These will be quoted correctly in case they contain spaces.
452  // \param distinct can be used in conjunction with a selection of specific columns in order
453  // to retrieve only rows of data with different values for the column(s).
454  if (fInserting) {
455  Error("database::select_data",
456  "data insertion in progress; call end_data_insertion() before retrieving data");
457  return false;
458  }
459  if (fSelecting) {
460  Error("database::select_data",
461  "data retrieval already in progress; call get_next_result() until it returns false before making new selection");
462  return false;
463  }
464  // set up SQL statement for data retrieval
465  fBulkTable = &fTables[table.Data()];
466 
467  KVString column_selection(""), _columns(columns);
468  if (columns == "*") {
469  column_selection = "*";
470  distinct = false; // don't allow 'SELECT DISTINCT * FROM ....' (?)
471  fSelectedColumns = "*";
472  }
473  else {
474  if (distinct) column_selection = "DISTINCT ";
475  fSelectedColumns = "";
476  // put quoted column names in column_selection, add plain column names to fSelectedColumns
477  _columns.Begin(",");
478  int i(0);
479  while (!_columns.End()) {
480  KVString colnam = _columns.Next();
481  if (i) {
482  column_selection += ", ";
483  fSelectedColumns += ", ";
484  }
485  column_selection += Form("\"%s\"", colnam.Data());
486  fSelectedColumns += colnam.Data();
487  ++i;
488  }
489  }
490 
491  TString cond = Form("SELECT %s FROM \"%s\"", column_selection.Data(), table.Data());
492  if (selection != "") cond += Form(" WHERE %s", selection.Data());
493  if (anything_else != "") cond += Form(" %s", anything_else.Data());
494  fSQLstmt.reset(fDBserv->Statement(cond));
495  if (fSQLstmt.get() == nullptr) {
496  Error("database::select_data", "problem processing : %s", cond.Data());
497  fSelecting = false;
498  fBulkTable = nullptr;
499  return false;
500  }
501  fSQLstmt->EnableErrorOutput();
502  if (fSQLstmt->Process()) {
503  fSQLstmt->StoreResult();
504  fSelecting = true;
505  fEmptyResultSet = false;
506  return true;
507  }
508  else if (!fSQLstmt->IsError()) {
509  // query ok, no results correspond to selection
510  fSQLstmt->StoreResult();
511  fSelecting = true;
512  fEmptyResultSet = true;
513  return true;
514  }
515  fSelecting = false;
516  fBulkTable = nullptr;
517  return false;
518  }
519 
520 
521 
525 
526  bool database::get_next_result() const
527  {
528  // Retrieve next result row resulting from previous call to select_data()
529  // \returns kFALSE when no more data is retrieved
530 
531  if (fInserting) {
532  Error("database::get_next_result",
533  "data insertion in progress; call end_data_insertion() then select_data() before this method");
534  return false;
535  }
536  if (!fSelecting) {
537  Error("database::get_next_result",
538  "no data retrieval in progress; select_data() must be called and return true before calling this method");
539  return false;
540  }
541  if (!fEmptyResultSet && fSQLstmt->NextResultRow()) {
542  // set column data
543  if (fSelectedColumns == "*") {
544  for (int i = 0; i < fBulkTable->number_of_columns(); ++i) {
545  (*fBulkTable)[i].set_data_from_statement(fSQLstmt.get());
546  }
547  }
548  else {
549  // only read data for selected columns
550  int idx = 0;
551  for (int i = 0; i < fBulkTable->number_of_columns(); ++i) {
552  if (fSelectedColumns.Contains((*fBulkTable)[i].name())) {
553  (*fBulkTable)[i].set_data_from_statement(fSQLstmt.get(), idx);
554  ++idx;
555  }
556  }
557  }
558  return kTRUE;
559  }
560  fBulkTable = nullptr;
561  fSelecting = false;
562  return kFALSE;
563  }
564 
565 
566 
570 
571  KVNumberList database::get_integer_list(const TString& table, const TString& column, const TString& selection, const TString& anything_else)
572  {
573  // \note Only for INTEGER columns!
574  // Fill KVNumberList with all `DISTINCT` values of "column" (only 1 column name at a time) for given selection
575 
576  KVNumberList result;
577  if (select_data(table, column, selection, true, anything_else)) {
578  while (get_next_result()) {
579  result.Add((*this)[table][column].get_data<int>());
580  }
581  }
582  return result;
583  }
584 
585 
586 
591 
592  TString database::get_string_list(const TString& table, const TString& column, const TString& selection, const TString& anything_else)
593  {
594  // \note Only for TEXT columns!
595  // Fill TString with comma-separated list of values of "column" (only 1 column name at a time) for given selection
596  // \note Any NULL entries will be ignored
597 
598  TString result;
599  if (select_data(table, column, selection, false, anything_else)) {
600  while (get_next_result()) {
601  if ((*this)[table][column].is_null()) continue;
602  if (result != "") result += ",";
603  result += (*this)[table][column].get_data<TString>();
604  }
605  }
606  return result;
607  }
608 
609 
610 
615 
616  KVNameValueList database::get_name_value_list(const TString& tablename, const TString& name_column, const TString& value_column, const TString& selection, const TString& anything_else)
617  {
618  // Fill KVNameValueList with selected rows from table, adding for each row a parameter with the
619  // name contained in "name_column" (must be of type `TEXT`) and the value contained in "value_column"
620  // (can be `INTEGER`, `REAL`, or `TEXT`)
621 
622  KVNameValueList result;
623  if (select_data(tablename, Form("%s,%s", name_column.Data(), value_column.Data()), selection, false, anything_else)) {
624  table& tb = (*this)[tablename];
625  column& nom = tb[name_column];
626  column& val = tb[value_column];
627  while (get_next_result()) {
628  result.SetValue(nom.get_data<TString>(), val.data());
629  }
630  }
631  return result;
632  }
633 
634 
635 
639 
640  TGraph* database::create_graph(const TString& tablename, const TString& Xcolumn, const TString& Ycolumn, const TString& selection)
641  {
642  // Create and fill a TGraph from values Xcolumn and Ycolumn in table,
643  // using the selection if required
644 
645  if (select_data(tablename, Form("%s,%s", Xcolumn.Data(), Ycolumn.Data()), selection)) {
646  TGraph* g = new TGraph;
647  int i = 0;
648  table& tb = (*this)[tablename];
649  column& Xcol = tb[Xcolumn];
650  column& Ycol = tb[Ycolumn];
651  while (get_next_result()) {
652  g->SetPoint(i++, Xcol.get_data<double>(), Ycol.get_data<double>());
653  }
654  return g;
655  }
656  return nullptr;
657  }
658 
659 
660 
663 
664  void database::clear_table(const TString& name)
665  {
666  // Delete all data from table
667  delete_data(name);
668  }
669 
670 
671 
679 
680  int database::count(const TString& table, const TString& column, const TString& selection, bool distinct) const
681  {
682  // Returns number of rows in table for which selection holds true:
683  //
684  // + if column="*" all rows are included
685  // + if a column name is given, only rows with a non-NULL value for column are counted
686  // + if distinct=false, count all rows including those with the same value of column
687  // + if distinct=true, count the number of different values of column
688 
689  TString qry = "SELECT count(";
690  if (distinct) qry += "DISTINCT ";
691  qry += Form("\"%s\"", column.Data());
692  qry += ") FROM '";
693  qry += table;
694  qry += "'";
695  if (selection != "") {
696  qry += " WHERE ";
697  qry += selection;
698  }
699 
700  unique_ptr<TSQLResult> result(fDBserv->Query(qry));
701  unique_ptr<TSQLRow> row(result->Next());
702  TString number = row->GetField(0);
703  return number.Atoi();
704  }
705 
706 
707 
716 
717  bool database::update(const TString& table, const TString& columns, const TString& selection)
718  {
719  // update the given columns of an entry in the table corresponding to selection (if given)
720  // the current values of the data members of the columns will be used
721  //
722  // This is equivalent to
723  //~~~~
724  // UPDATE [table] SET col1=newval,col2=newval,... [WHERE [selection]]
725  //~~~~
726 
727  if (fInserting) {
728  Error("database::update",
729  "data insertion in progress; call end_data_insertion() before doing anything else");
730  return false;
731  }
732  if (fSelecting) {
733  Error("database::update",
734  "data retrieval in progress; call get_next_result() until it returns false before doing anything else");
735  return false;
736  }
737 
738  fBulkTable = &fTables[table.Data()];
739  TString query = Form("UPDATE \"%s\" SET ", table.Data());
740  int ncol = fBulkTable->number_of_columns();
741  int idx = 0;
742  for (int i = 0; i < ncol; ++i) {
743  if (columns.Contains((*fBulkTable)[i].name())) {
744  if (idx) query += ",";
745  query += Form("\"%s\"", (*fBulkTable)[i].name());
746  query += "=?";
747  ++idx;
748  }
749  }
750  if (selection != "") query += Form(" WHERE %s", selection.Data());
751  //std::cout << query << std::endl;
752  fSQLstmt.reset(fDBserv->Statement(query));
753  fSQLstmt->NextIteration();
754  idx = 0;
755  for (int i = 0; i < ncol; ++i) {
756  if (columns.Contains((*fBulkTable)[i].name())) {
757  (*fBulkTable)[i].set_data_in_statement(fSQLstmt.get(), idx);
758  ++idx;
759  }
760  }
761  return (fSQLstmt->Process());
762  }
763 
764 
765 
774 
775  void database::delete_data(const TString& table, const TString& selection)
776  {
777  // delete rows from the table corresponding to selection
778  //
779  // This is equivalent to
780  //~~~~
781  // DELETE FROM [table] WHERE [selection]
782  //~~~~
783  // With no selection, deletes all rows of table (clear_table())
784 
785  TString query = Form("DELETE FROM \"%s\"", table.Data());
786  if (selection != "") query += Form(" WHERE %s", selection.Data());
787  fDBserv->Exec(query);
788  }
789 
790 
791 
795 
796  column& database::add_column(const TString& table, const TString& name, const TString& type)
797  {
798  // add column to existing table
799  // \returns reference to new column
800  TString query = Form("ALTER TABLE \"%s\" ADD COLUMN \"%s\" %s", table.Data(), name.Data(), type.Data());
801  fDBserv->Exec(query);
802  return (*this)[table].add_column(name, type);
803  }
804 
805 
806 
810 
811  void database::add_missing_columns(const TString& _table_, const KVNameValueList& l)
812  {
813  // add to table any columns which are defined in the list but don't exist
814  // \note cannot be called during data insertion or retrieval!!!
815 
816  if (fInserting) {
817  Error("database::add_missing_columns",
818  "data insertion in progress; call end_data_insertion() before doing anything else");
819  return;
820  }
821  if (fSelecting) {
822  Error("database::add_missing_columns",
823  "data retrieval in progress; call get_next_result() until it returns false before doing anything else");
824  return;
825  }
826  int ipar = l.GetNpar();
827  table& tab = (*this)[_table_];
828  for (int i = 0; i < ipar; ++i) {
829  KVNamedParameter* par = l.GetParameter(i);
830  if (!tab.has_column(par->GetName())) add_column(_table_, par->GetName(), par->GetSQLType());
831  }
832  }
833 
834 
835 
843 
844  void database::copy_table_data(const TString& source, const TString& destination, const TString& columns, const TString& selection)
845  {
846  // Copy all selected data in 'source' table to 'destination'
847  //
848  // If the columns of the two tables are not identical, specify the columns to copy in 'columns'
849  // (comma-separated list)
850  // \note SQLite will not allow copy if the number of selected columns from 'source' is not
851  // exactly equal to the number of columns in 'destination'
852 
853  TString COLUMNS = columns;
854  if (COLUMNS != "*") {
855  // quote all column names
856  COLUMNS = "";
857  KVString _columns(columns);
858  _columns.Begin(",");
859  while (!_columns.End()) {
860  if (COLUMNS != "") COLUMNS += ", ";
861  COLUMNS += Form("\"%s\"", _columns.Next(kTRUE).Data());
862  }
863  }
864  TString query = Form("INSERT INTO \"%s\" SELECT %s FROM %s", destination.Data(), COLUMNS.Data(), source.Data());
865  if (selection != "") query += Form(" WHERE %s", selection.Data());
866  fDBserv->Exec(query);
867  }
868 
869 
870 
872 
873  void column::init_type_map()
874  {
875  inv_type_map[KVSQLite::column_type::REAL] = "REAL";
876  inv_type_map[KVSQLite::column_type::INTEGER] = "INTEGER";
877  inv_type_map[KVSQLite::column_type::TEXT] = "TEXT";
878  inv_type_map[KVSQLite::column_type::BLOB] = "BLOB";
879  }
880 
881 
882 
884 
885  const char* column::_type()
886  {
887  return inv_type_map[fNameType.second];
888  }
889 
890 
891 
893 
894  template<> void column::set_data(const KVNamedParameter& x)
895  {
896  fData.Set(x.GetName(), x);
897  fIsNull = false;
898  }
899 
900 
901 
907 
908  void column::set_data_in_statement(TSQLStatement* s, int idx) const
909  {
910  // set value of parameter in SQLite statement corresponding to this column
911  // \param idx if given, use it as the statement parameter index instead of
912  // the column's index in the table (case where not all columns are treated
913  // in the statement)
914 
915  if (idx < 0) idx = index();
916  if (fIsNull) {
917  // null parameter
918  s->SetNull(idx);
919  return;
920  }
921  switch (type()) {
923  s->SetDouble(idx, fData.GetDouble());
924  break;
926  s->SetInt(idx, fData.GetInt());
927  break;
929  s->SetString(idx, fData.GetString(), -1);
930  break;
932  s->SetBinary(idx, fBlob, fBlobSize);
933  break;
934  default:
935  break;
936  }
937  }
938 
939 
950 
951  void column::set_data_from_statement(TSQLStatement* s, int idx)
952  {
953  // set value of column according to value of parameter in statement
954  //
955  // any column which has a NULL value will be given value 0, 0.0 or ""
956  // (for `INTEGER`, `REAL` or `TEXT` type, respectively): use column::is_null()
957  // to check if this corresponds to a null column value.
958  //
959  // \param idx if given, use it as the statement parameter index instead of
960  // the column's index in the table (case where not all columns are treated
961  // in the statement)
962 
963  if (idx < 0) idx = index();
964  fIsNull = s->IsNull(idx);
965  switch (type()) {
967  fData.Set(fIsNull ? 0.0 : s->GetDouble(idx));
968  break;
970  fData.Set(fIsNull ? 0 : s->GetInt(idx));
971  break;
973  fData.Set(fIsNull ? "" : s->GetString(idx));
974  break;
976  if (fIsNull) {
977  fBlobSize = 0;
978  }
979  else {
980  if (!fBlob) fBlob = (void*) new unsigned char[256];
981  s->GetBinary(idx, fBlob, fBlobSize);
982  }
983  break;
984  default:
985  break;
986  }
987  }
988 
989 
990 
994 
995  void column::set_foreign_key(const TString& _table, const TString& _column)
996  {
997  // declare this column to be a foreign key i.e. linked to the given
998  // _column name in another _table
999  fForeignKey = true;
1000  fFKtable = _table;
1001  fFKcolumn = _column;
1002  }
1003 
1004 
1005 
1009 
1010  void column::set_foreign_key(const table& _table, const column& _column)
1011  {
1012  // declare this column to be a foreign key i.e. linked to the given
1013  // _column name in another _table
1014  fForeignKey = true;
1015  fFKtable = _table.name();
1016  fFKcolumn = _column.name();
1017  }
1018 
1019 
1020 
1022 
1023  void table::init_type_map()
1024  {
1025  type_map["REAL"] = KVSQLite::column_type::REAL;
1026  type_map["INTEGER"] = KVSQLite::column_type::INTEGER;
1027  type_map["TEXT"] = KVSQLite::column_type::TEXT;
1028  type_map["BLOB"] = KVSQLite::column_type::BLOB;
1029  }
1030 
1031 
1032 
1035 
1036  void table::show_columns() const
1037  {
1038  // print list of columns
1039  std::cout << "Columns in table:" << std::endl;
1040 #ifdef WITH_CPP11
1041  for (auto it = fColumns.begin(); it != fColumns.end(); ++it) {
1042 #else
1043  for (std::vector<KVSQLite::column>::const_iterator it = fColumns.begin(); it != fColumns.end(); ++it) {
1044 #endif
1045  std::cout << "\t" << it->name() << " [" << it->type_name() << "]" << std::endl;
1046  }
1047  }
1048 
1049 
1050 
1055 
1056  column& table::add_column(const column& c)
1057  {
1058  // add column to table
1059  // \return reference to added column.
1060  // \note cannot be used for existing table in database: see database::add_column()
1061  fColumns.push_back(c);
1062  fColMap[c.name()] = c.index();
1063  return fColumns.back();
1064  }
1065 
1066 
1067 
1072 
1073  column& table::add_column(const TString& name, const TString& type)
1074  {
1075  // add column to table
1076  // \return reference to added column
1077  // \note cannot be used for existing table in database: see database::add_column()
1078  return add_column(name, type_map[type]);
1079  }
1080 
1081 
1082 
1091 
1092  const column& table::add_primary_key(const TString& name)
1093  {
1094  // add a PRIMARY KEY column to the table
1095  // \returns reference to primary key (cannot be modified)
1096  //
1097  // \note by default this is an `INTEGER` type column
1098  //
1099  // \note as it is auto-incremented with each inserted row, it should not
1100  // be included in TSQLStatement used to write data to db
1101 
1102  column& c = add_column(name, KVSQLite::column_type::INTEGER);
1103  c.set_constraint("PRIMARY KEY");
1104  return c;
1105  }
1106 
1107 
1108 
1113 
1114  const column& table::add_foreign_key(const TString& name, const TString& other_table, const TString& other_column)
1115  {
1116  // add a foreign key to the table, which is an INTEGER reference to
1117  // another column in another table.
1118  // \returns reference to key (cannot be modified)
1119 
1120  column& c = add_column(name, KVSQLite::column_type::INTEGER);
1121  c.set_foreign_key(other_table, other_column);
1122  return c;
1123  }
1124 
1125 
1126 
1131 
1132  const column& table::add_foreign_key(const TString& name, const table& other_table, const column& other_column)
1133  {
1134  // add a foreign key to the table, which is an INTEGER reference to
1135  // another column in another table.
1136  // \returns reference to key (cannot be modified)
1137 
1138  column& c = add_column(name, KVSQLite::column_type::INTEGER);
1139  c.set_foreign_key(other_table, other_column);
1140  return c;
1141  }
1142 
1143 
1144 
1148 
1149  int table::check_columns(const KVNameValueList& l)
1150  {
1151  // make sure that all parameters in the list have corresponding columns in the table
1152  // \returns the number of columns to be added
1153 
1154  int ncols = 0;
1155  int ipar = l.GetNpar();
1156  for (int i = 0; i < ipar; ++i) {
1157  KVNamedParameter* par = l.GetParameter(i);
1158  if (!has_column(par->GetName())) ncols++;
1159  }
1160  return ncols;
1161  }
1162 
1163 
1164 
1171 
1172  void table::prepare_data(const KVNameValueList& l, const KVNamedParameter* null_value)
1173  {
1174  // fill all columns in table with data contained in KVNameValueList parameters having the same name.
1175  //
1176  // any columns which do not appear in the KVNameValueList will be set to 'null'
1177  //
1178  // if required, any parameters with the same type&value as "null_value" will be set to 'null' too
1179 
1180  for (int i = 0; i < number_of_columns(); ++i) {
1181  KVNamedParameter* p = l.FindParameter((*this)[i].name());
1182  if (p && !(null_value && p->HasSameValueAs(*null_value)))
1183  (*this)[i].set_data(*p);
1184  else
1185  (*this)[i].set_null();
1186  }
1187  }
1188 
1189 
1190 
1193 
1194  void table::set_all_columns_null()
1195  {
1196  // set the value of all columns in the table to NULL
1197  for (int i = 0; i < number_of_columns(); ++i) {
1198  (*this)[i].set_null();
1199  }
1200  }
1201 
1202 
1203  //____________________________________________________________________________//
1204 
1205 }
1206 
Defines macros for standard error messages.
ClassImp(KVPartitionList) void KVPartitionList
Initialisation.
ROOT::R::TRInterface & r
#define f(i)
#define c(i)
const Bool_t kFALSE
const Bool_t kTRUE
int type
char * Form(const char *fmt,...)
Handles lists of named parameters with different types, a list of KVNamedParameter objects.
void SetValue(const Char_t *name, value_type value)
A generic named parameter storing values of different types.
Bool_t HasSameValueAs(const KVNamedParameter &) const
const Char_t * GetSQLType() const
Strings used to represent a set of ranges of values.
Definition: KVNumberList.h:83
void Add(Int_t)
Add value 'n' to the list.
const char * name() const
Definition: SQLiteDB.h:148
T get_data() const
Definition: SQLiteDB.h:239
const KVNamedParameter & data() const
Definition: SQLiteDB.h:234
Interface to ROOT SQLite database backend ,.
Definition: SQLiteDB.h:374
const char * name() const
Definition: SQLiteDB.h:280
bool is_temporary() const
Definition: SQLiteDB.h:314
column & add_column(const KVSQLite::column &c)
Definition: SQLiteDB.cpp:1056
int number_of_columns() const
Definition: SQLiteDB.h:363
bool has_column(const TString &name) const
Definition: SQLiteDB.h:333
Extension of ROOT TString class which allows backwards compatibility with ROOT v3....
Definition: KVString.h:72
void Begin(TString delim) const
Definition: KVString.cpp:562
Bool_t End() const
Definition: KVString.cpp:625
KVString Next(Bool_t strip_whitespace=kFALSE) const
Definition: KVString.cpp:675
virtual const char * GetName() const
virtual const char * GetName() const
const char * GetTypeName() const
virtual Int_t GetFieldCount()=0
virtual const char * GetFieldName(Int_t field)=0
virtual TSQLRow * Next()=0
static TSQLServer * Connect(const char *db, const char *uid, const char *pw)
Int_t Atoi() const
const char * Data() const
void Form(const char *fmt,...)
Bool_t Contains(const char *pat, ECaseCompare cmp=kExact) const
Double_t x[n]
for(Int_t i=0;i< n;i++)
const long double s
Definition: KVUnits.h:94
const long double g
masses
Definition: KVUnits.h:72
void Error(const char *location, const char *va_(fmt),...)
auto * l