gambit is hosted by Hepforge, IPPP Durham
GAMBIT  v1.5.0-2191-ga4742ac
a Global And Modular Bsm Inference Tool
sqlitereader.cpp
Go to the documentation of this file.
1 // GAMBIT: Global and Modular BSM Inference Tool
2 // *********************************************
19 
23 
24 // Activate extra debug output on errors
25 #define SQL_DEBUG
26 
27 namespace Gambit
28 {
29  namespace Printers
30  {
31 
33  : BaseReader()
34  , SQLiteBase()
35  , stmt(NULL)
36  , current_dataset_index(0)
37  , current_point(nullpoint)
38  {
39  // Open the database file
40  open_db(options.getValue<std::string>("file"),'r');
41 
42  // Tell the base class what table we want to access
43  set_table_name(options.getValue<std::string>("table"));
44 
45  // Make sure that the requested table exists
47 
48  // Determine the types of all columns in the input table
51 
52  // Set up the reader loop
53  reset();
54  }
55 
57  {
58  if(not eoi() and stmt!=NULL)
59  {
60  // Finalise any existing loop
61  sqlite3_finalize(stmt);
62  }
63  }
64 
66  template<>
67  long long int SQLiteReader::get_sql_col<long long int>(const std::string& col_name)
68  {
69  return sqlite3_column_int64(stmt, get_col_i(col_name));
70  }
71 
72  template<>
73  double SQLiteReader::get_sql_col<double>(const std::string& col_name)
74  {
75  return sqlite3_column_double(stmt, get_col_i(col_name));
76  }
77 
78  template<>
79  std::string SQLiteReader::get_sql_col<std::string>(const std::string& col_name)
80  {
81  char* p = (char*)sqlite3_column_text(stmt, get_col_i(col_name));
82  if(p==NULL)
83  {
84  std::stringstream err;
85  err<<"Pointer returned by sqlite3_column_text was NULL!";
86  printer_error().raise(LOCAL_INFO, err.str());
87  }
88  return std::string(p);
89  }
91 
92  // Determine mapping from column names to indices
94  {
95  // We will SELECT the columns according to the results of get_column_info,
96  // so there is no need to directly inspect the table again.
98  column_map.clear();
99  std::size_t i=0;
100  for(auto it=column_types.begin(); it!=column_types.end(); ++it)
101  {
102  std::string col_name = it->first;
103  column_map[col_name] = i;
104  i++;
105  }
106  }
107 
109 
112  {
113  if(not eoi() and stmt!=NULL)
114  {
115  // Finalise the previous loop so we can start a new one
116  sqlite3_finalize(stmt);
117  }
118 
119  // Reset loop variables
122  eoi_flag = false;
123 
124  std::stringstream sql;
125  sql<<"SELECT ";
126  for(auto it=column_types.begin(); it!=column_types.end(); ++it)
127  {
128  std::string col_name = it->first;
129  sql<<"`"<<col_name<<"`"<<comma_unless_last(it,column_types);
130  }
131  sql<<" FROM "<<get_table_name();
132 
133  /* Execute SQL statement and iterate through results*/
134  int rc = sqlite3_prepare_v2(get_db(), sql.str().c_str(), -1, &stmt, NULL);
135  if (rc != SQLITE_OK) {
136  std::stringstream err;
137  err<<"Encountered SQLite error while preparing to read data from previous run: "<<sqlite3_errmsg(get_db());
138 #ifdef SQL_DEBUG
139  err << " The attempted SQL statement was:"<<std::endl;
140  err << sql.str() << std::endl;
141 #endif
142  printer_error().raise(LOCAL_INFO, err.str());
143  }
144 
145  // Read first row
147 
148  if(eoi())
149  {
150  std::stringstream err;
151  err<<"Immediately reached end of input after beginning to loop through table "<<get_table_name()<<" in file "<<get_database_file()<<"! Perhaps the table is empty?";
152  printer_error().raise(LOCAL_INFO, err.str());
153  }
154  }
155 
157  std::size_t SQLiteReader::get_col_i(const std::string& col_name)
158  {
159  auto it = column_map.find(col_name);
160  if(it==column_map.end())
161  {
162  std::stringstream err;
163  err<<"Attempted to retrieve data for column with name '"<<col_name<<"' using SQLiteReader, however this column does not seem to exist in the table we are reading!";
164  printer_error().raise(LOCAL_INFO, err.str());
165  }
166  return it->second;
167  }
168 
169 
172  {
173  std::stringstream sql;
174  sql<<"SELECT COUNT(pairID) FROM "<<get_table_name()<<";";
175  sqlite3_stmt *temp_stmt;
176  int rc = sqlite3_prepare_v2(get_db(), sql.str().c_str(), -1, &temp_stmt, NULL);
177  if (rc != SQLITE_OK) {
178  std::stringstream err;
179  err<<"Encountered SQLite error while preparing to measure length of input table: "<<sqlite3_errmsg(get_db());
180  printer_error().raise(LOCAL_INFO, err.str());
181  }
182  rc = sqlite3_step(temp_stmt);
183  cout_row(temp_stmt); // DEBUG
184  if (rc != SQLITE_ROW) {
185  std::stringstream err;
186  err<<"Encountered SQLite error while attempting to measure length of input table: "<<sqlite3_errmsg(get_db());
187  printer_error().raise(LOCAL_INFO, err.str());
188  }
189  long long int rowcount = sqlite3_column_int64(temp_stmt, 0);
190  sqlite3_finalize(temp_stmt);
191  if(rowcount<0)
192  {
193  std::stringstream err;
194  err<<"Row count for input table was measured to be negative ("<<rowcount<<")! This clearly makes no sense and is probably a bug in the SQLiteReader class, please report it.";
195  printer_error().raise(LOCAL_INFO, err.str());
196  }
197  return rowcount;
198  }
199 
202  {
203  if(eoi())
204  {
205  std::stringstream err;
206  err<<"Attempted to move SQLiteReader to next row of input table, but eoi() has been reached! This should have been checked by whatever code called this function!";
207  printer_error().raise(LOCAL_INFO, err.str());
208  }
209  else if(stmt==NULL)
210  {
211  std::stringstream err;
212  err<<"Attempted to move SQLiteReader to next row of input table, but no sql statement has been prepared for iteration!";
213  printer_error().raise(LOCAL_INFO, err.str());
214  }
215  else
216  {
217  // Process the next row
218  int rc = sqlite3_step(stmt);
219  if(rc==SQLITE_ROW)
220  {
221  std::size_t rank = sqlite3_column_int64(stmt, get_col_i("MPIrank"));
222  std::size_t pID = sqlite3_column_int64(stmt, get_col_i("pointID"));
223  current_point = PPIDpair(pID,rank);
224  }
225  else if(rc==SQLITE_DONE)
226  {
227  // We are at the end of the dataset!
228  eoi_flag = true;
230  sqlite3_finalize(stmt);
231  }
232  else
233  {
234  // Not the next row, and not the end, something bad has happened.
235  std::stringstream err;
236  err<<"Encountered SQLite error while processing input file: "<<sqlite3_errmsg(get_db());
237  printer_error().raise(LOCAL_INFO, err.str());
238  }
239  }
240  }
241 
244  {
247  return get_current_point();
248  }
249 
252  {
253  if(eoi())
254  {
255  // End of data, return nullpoint;
257  }
258  return current_point;
259  }
260 
261  // Get a linear index which corresponds to the current rank/ptID pair in the iterative sense
263  {
264  return current_dataset_index;
265  }
266 
269  {
270  return eoi_flag;
271  }
272 
275  std::size_t SQLiteReader::get_type(const std::string& label)
276  {
277  // Need to match SQL datatype to a printer type ID code.
278  // In principle we may like to retrieve a certain type of data in a fancy way,
279  // as with ModelParameters or vectors, however we can't really do that in an
280  // automated way because this higher-level information is lost during output.
281  // So the type matching has to be of a basic sort, i.e. individual ModelParameters
282  // elements will be identified as 'double' and so on. But if they are stored that
283  // way in the output, then we should be able to copy them that way too (which is
284  // the main usage of this get_type function), so this should be ok to do.
285  // Currently we only store data in basic types, so those are all that this
286  // function needs to retrieve.
287 
288  // First find out the SQL type for the column with this label
289  auto it = column_types.find(label);
290  if(it==column_types.end())
291  {
292  std::stringstream err;
293  err<<"Column with name '"<<label<<"' is not registered in the column_types map!";
294  printer_error().raise(LOCAL_INFO,err.str());
295  }
296  std::string coltype=it->second;
297 
298  if(coltype=="NULL")
299  {
300  std::stringstream err;
301  err<<"Column with name '"<<label<<"' is registered as having type 'NULL'! This doesn't make sense, only individual table slots with missing data should have type NULL, it should not be the 'affinity' for a whole column. This indicates a bug in the SQLiteReader object, please report it";
302  printer_error().raise(LOCAL_INFO,err.str());
303  }
304 
305  // Now match the SQL datatypes to Printer type IDs (via appropriate C++ types)
306  // TODO might need more careful checking, not sure if e.g. INTEGER type name will
307  // always be retrieved as INTERGER (as opposed to say INT or something else)
308  std::size_t typeID;
309  #define GET_SQL_TYPE_CASES(r,data,elem) \
310  if( SQLite_equaltypes(coltype,cpp2sql<elem>()) )\
311  { \
312  typeID = getTypeID<elem>(); \
313  } \
314  else
315  BOOST_PP_SEQ_FOR_EACH(GET_SQL_TYPE_CASES, _, SQLITE_CPP_TYPES)
316  #undef GET_SQL_TYPE_CASES
317  {
318  std::ostringstream err;
319  err << "Did not recognise retrieved SQL type for data label '"<<label<<"' (its SQL type is registered as '"<<coltype<<"')! This may indicate a bug in the SQLiteReader class, please report it.";
320  printer_error().raise(LOCAL_INFO,err.str());
321  }
322  if(typeID==0)
323  {
324  std::ostringstream err;
325  err << "Did not recognise retrieved Printer type for data label '"<<label<<"' (its SQL type is registered as '"<<coltype<<"')! This may indicate a bug in the Printer system, please report it.";
326  printer_error().raise(LOCAL_INFO,err.str());
327  }
328  return typeID;
329  }
330 
332  std::set<std::string> SQLiteReader::get_all_labels()
333  {
334  std::set<std::string> out;
335  for (auto it = column_map.begin(); it != column_map.end(); ++it)
336  {
337  out.insert(it->first);
338  }
339  return out;
340  }
341 
343 
344  }
345 }
void set_table_name(const std::string &table_name)
Definition: sqlitebase.cpp:324
SQLite printer retriever class definitions This is a class accompanying the SQLitePrinter which takes...
EXPORT_SYMBOLS error & printer_error()
Printer errors.
std::map< std::string, std::size_t, Utils::ci_less > column_map
#define LOCAL_INFO
Definition: local_info.hpp:34
virtual std::set< std::string > get_all_labels()
Get labels of all datasets in the linked group.
General small utility functions.
TYPE getValue(const args &... keys) const
#define SQLITE_CPP_TYPES
Definition: sqlitebase.hpp:53
virtual bool eoi()
Check if &#39;current point&#39; is past the end of the datasets (and thus invalid!)
virtual ulong get_dataset_length()
Get length of input dataset.
void move_to_next_point()
Move the SQL loop ahead one.
#define GET_SQL_TYPE_CASES(r, data, elem)
virtual std::size_t get_type(const std::string &label)
Get type information for a data entry, i.e.
SQLiteReader(const Options &options)
Copies of boost headers that are required but that may be too recent to be present on the user&#39;s mach...
std::string comma_unless_last(Iter it, const Cont &c)
Definition: sqlitebase.hpp:82
BASE PRINTER CLASS.
unsigned long int ulong
std::map< std::string, std::string, Utils::ci_less > get_column_info()
Definition: sqlitebase.cpp:300
virtual PPIDpair get_current_point()
Get current rank/ptID pair in data file.
virtual void reset()
Base class virtual interface functions.
void open_db(const std::string &, char access='r')
Definition: sqlitebase.cpp:164
std::size_t get_col_i(const std::string &col_name)
Safely access the column_map and throw informative error when column is missing.
virtual PPIDpair get_next_point()
Get next rank/ptID pair in data file.
void cout_row(sqlite3_stmt *tmp_stmt)
Definition: sqlitebase.cpp:327
EXPORT_SYMBOLS const PPIDpair nullpoint
Define &#39;nullpoint&#39; const.
SQLite base class for both reader and writer.
Definition: sqlitebase.hpp:93
pointID / process number pair Used to identify a single parameter space point
TODO: see if we can use this one:
Definition: Analysis.hpp:33
A small wrapper object for &#39;options&#39; nodes.
std::map< std::string, std::string, Utils::ci_less > column_types