CYCLUS
Loading...
Searching...
No Matches
sqlite_back.cc
Go to the documentation of this file.
1#include "sqlite_back.h"
2
3#include <iomanip>
4#include <sstream>
5
6#include <boost/lexical_cast.hpp>
7#include <boost/uuid/uuid_io.hpp>
8#include <boost/algorithm/string.hpp>
9#include <boost/archive/tmpdir.hpp>
10#include <boost/archive/xml_iarchive.hpp>
11#include <boost/archive/xml_oarchive.hpp>
12#include <boost/serialization/base_object.hpp>
13#include <boost/serialization/utility.hpp>
14#include <boost/serialization/list.hpp>
15#include <boost/serialization/set.hpp>
16#include <boost/serialization/vector.hpp>
17#include <boost/serialization/map.hpp>
18#include <boost/serialization/assume_abstract.hpp>
19
20
21#include "blob.h"
22#include "datum.h"
23#include "error.h"
24#include "logger.h"
25
26namespace cyclus {
27
28std::vector<std::string> split(const std::string& s, char delim) {
29 std::vector<std::string> elems;
30 std::stringstream ss(s);
31 std::string item;
32 while (std::getline(ss, item, delim)) {
33 elems.push_back(item);
34 }
35 return elems;
36}
37
39 try {
40 Flush();
41 db_.close();
42 } catch (Error err) {
43 CLOG(LEV_ERROR) << "Error in SqliteBack destructor: " << err.what();
44 }
45}
46
47SqliteBack::SqliteBack(std::string path) : db_(path) {
48 path_ = path;
49 db_.open();
50
51 db_.Execute("PRAGMA synchronous=OFF;");
52 db_.Execute("PRAGMA journal_mode=MEMORY;");
53 db_.Execute("PRAGMA temp_store=MEMORY;");
54
55 // cache pre-existing table names
57 stmt = db_.Prepare("SELECT name FROM sqlite_master WHERE type='table';");
58
59 for (int i = 0; stmt->Step(); ++i) {
60 tbl_names_.insert(stmt->GetText(0, NULL));
61 }
62
63 if (tbl_names_.count("FieldTypes") == 0) {
64 std::string cmd = "CREATE TABLE IF NOT EXISTS FieldTypes";
65 cmd += "(TableName TEXT,Field TEXT,Type INTEGER);";
66 db_.Execute(cmd);
67 }
68}
69
71 db_.Execute("BEGIN TRANSACTION;");
72 try {
73 for (DatumList::iterator it = data.begin(); it != data.end(); ++it) {
74 std::string tbl = (*it)->title();
75 if (tbl_names_.count(tbl) == 0) {
76 CreateTable(*it);
77 }
78 if (stmts_.count(tbl) == 0) {
79 BuildStmt(*it);
80 }
81 WriteDatum(*it);
82 }
83 } catch (ValueError err) {
84 db_.Execute("END TRANSACTION;");
85 throw ValueError(err.what());
86 }
87 db_.Execute("END TRANSACTION;");
88 Flush();
89}
90
92
93std::list<ColumnInfo> SqliteBack::Schema(std::string table) {
94 std::list<ColumnInfo> schema;
95 QueryResult qr = GetTableInfo(table);
96 for (int i = 0; i < qr.fields.size(); ++i) {
97 ColumnInfo info = ColumnInfo(table, qr.fields[i], i, qr.types[i], std::vector<int>());
98 schema.push_back(info);
99 }
100 return schema;
101}
102
103QueryResult SqliteBack::Query(std::string table, std::vector<Cond>* conds) {
104 QueryResult q = GetTableInfo(table);
105
106 std::stringstream sql;
107 sql << "SELECT * FROM " << table;
108 if (conds != NULL) {
109 sql << " WHERE ";
110 for (int i = 0; i < conds->size(); ++i) {
111 if (i > 0) {
112 sql << " AND ";
113 }
114 Cond c = (*conds)[i];
115 sql << c.field << " " << c.op << " ?";
116 }
117 }
118 sql << ";";
119
120 SqlStatement::Ptr stmt = db_.Prepare(sql.str());
121
122 if (conds != NULL) {
123 for (int i = 0; i < conds->size(); ++i) {
124 boost::spirit::hold_any v = (*conds)[i].val;
125 Bind(v, Type(v), stmt, i+1);
126 }
127 }
128
129 for (int i = 0; stmt->Step(); ++i) {
130 QueryRow r;
131 for (int j = 0; j < q.fields.size(); ++j) {
132 r.push_back(ColAsVal(stmt, j, q.types[j]));
133 }
134 q.rows.push_back(r);
135 }
136 return q;
137}
138
139std::map<std::string, DbTypes> SqliteBack::ColumnTypes(std::string table) {
140 QueryResult qr = GetTableInfo(table);
141 std::map<std::string, DbTypes> rtn;
142 for (int i = 0; i < qr.fields.size(); ++i)
143 rtn[qr.fields[i]] = qr.types[i];
144 return rtn;
145}
146
147std::set<std::string> SqliteBack::Tables() {
148 using std::set;
149 using std::string;
151 std::string sql = "SELECT name FROM sqlite_master WHERE type='table';";
153 stmt = db_.Prepare(sql);
154 while (stmt->Step()) {
155 rtn.insert(stmt->GetText(0, NULL));
156 }
157 rtn.erase("FieldTypes");
158 return rtn;
159}
160
162 return db_;
163}
164
165QueryResult SqliteBack::GetTableInfo(std::string table) {
166 std::string sql = "SELECT Field,Type FROM FieldTypes WHERE TableName = '" +
167 table + "';";
169 stmt = db_.Prepare(sql);
170
171 int i = 0;
173 for (i = 0; stmt->Step(); ++i) {
174 info.fields.push_back(stmt->GetText(0, NULL));
175 info.types.push_back((DbTypes)stmt->GetInt(1));
176 }
177 if (i == 0) {
178 throw ValueError("Invalid table name " + table);
179 }
180 return info;
181}
182
183std::string SqliteBack::Name() {
184 return path_;
185}
186
187void SqliteBack::BuildStmt(Datum* d) {
188 std::string name = d->title();
189 Datum::Vals vals = d->vals();
190 std::vector<DbTypes> schema;
191
192 schema.push_back(Type(vals[0].second));
193 std::string insert = "INSERT INTO " + name + " VALUES (?";
194 for (int i = 1; i < vals.size(); ++i) {
195 schema.push_back(Type(vals[i].second));
196 insert += ", ?";
197 }
198 insert += ");";
199
200 schemas_[name] = schema;
201 stmts_[name] = db_.Prepare(insert);
202}
203
204void SqliteBack::CreateTable(Datum* d) {
205 std::string name = d->title();
206 tbl_names_.insert(name);
207
208 Datum::Vals vals = d->vals();
209 Datum::Vals::iterator it = vals.begin();
210
211 std::stringstream types;
212 types << "INSERT INTO FieldTypes VALUES ('"
213 << name << "','" << it->first << "','"
214 << Type(it->second) << "');";
215 db_.Execute(types.str());
216
217 std::string cmd = "CREATE TABLE " + name + " (";
218 cmd += std::string(it->first) + " " + SqlType(it->second);
219 ++it;
220
221 while (it != vals.end()) {
222 cmd += ", " + std::string(it->first) + " " + SqlType(it->second);
223 std::stringstream types;
224 types << "INSERT INTO FieldTypes VALUES ('"
225 << name << "','" << it->first << "','"
226 << Type(it->second) << "');";
227 db_.Execute(types.str());
228 ++it;
229 }
230
231 cmd += ");";
232 db_.Execute(cmd);
233}
234
235void SqliteBack::WriteDatum(Datum* d) {
236 Datum::Vals vals = d->vals();
237 SqlStatement::Ptr stmt = stmts_[d->title()];
238 std::vector<DbTypes> schema = schemas_[d->title()];
239
240 for (int i = 0; i < vals.size(); ++i) {
241 boost::spirit::hold_any v = vals[i].second;
242 Bind(v, schema[i], stmt, i+1);
243 }
244
245 stmt->Exec();
246}
247
248void SqliteBack::Bind(boost::spirit::hold_any v, DbTypes type, SqlStatement::Ptr stmt,
249 int index) {
250
251// serializes the value v of type T and DBType D and binds it to stmt (inside
252// a case statement.
253// NOTE: Since we are archiving to a stringstream, the archive must be closed before
254// the stringstream, so we put it in its own scope. This first became an issue in
255// Boost v1.66.0. For more information, see http://boost.2283326.n4.nabble.com/the-boost-xml-serialization-to-a-stringstream-does-not-have-an-end-tag-tp2580772p2580773.html
256#define CYCLUS_COMMA ,
257#define CYCLUS_BINDVAL(D, T) \
258 case D: { \
259 T vect = v.cast<T>(); \
260 std::stringstream ss; \
261 { \
262 boost::archive::xml_oarchive ar(ss); \
263 ar & BOOST_SERIALIZATION_NVP(vect); \
264 } \
265 v = vect; \
266 std::string s = ss.str(); \
267 stmt->BindBlob(index, s.c_str(), s.size()); \
268 break; \
269 }
270
271 switch (type) {
272 case INT: {
273 stmt->BindInt(index, v.cast<int>());
274 break;
275 }
276 case BOOL: {
277 stmt->BindInt(index, v.cast<bool>());
278 break;
279 }
280 case DOUBLE: {
281 stmt->BindDouble(index, v.cast<double>());
282 break;
283 }
284 case FLOAT: {
285 stmt->BindDouble(index, v.cast<float>());
286 break;
287 }
288 case BLOB: {
289 std::string s = v.cast<Blob>().str();
290 stmt->BindBlob(index, s.c_str(), s.size());
291 break;
292 }
293 case STRING: {
294 stmt->BindText(index, v.cast<std::string>().c_str());
295 break;
296 }
297 case UUID: {
298 boost::uuids::uuid ui = v.cast<boost::uuids::uuid>();
299 stmt->BindBlob(index, ui.data, 16);
300 break;
301 }
302 CYCLUS_BINDVAL(SET_INT, std::set<int>);
303 CYCLUS_BINDVAL(SET_STRING, std::set<std::string>);
304 CYCLUS_BINDVAL(LIST_INT, std::list<int>);
305 CYCLUS_BINDVAL(LIST_STRING, std::list<std::string>);
306 CYCLUS_BINDVAL(VECTOR_INT, std::vector<int>);
307 CYCLUS_BINDVAL(VECTOR_DOUBLE, std::vector<double>);
308 CYCLUS_BINDVAL(VECTOR_STRING, std::vector<std::string>);
309 CYCLUS_BINDVAL(MAP_INT_DOUBLE, std::map<int CYCLUS_COMMA double>);
310 CYCLUS_BINDVAL(MAP_INT_INT, std::map<int CYCLUS_COMMA int>);
311 CYCLUS_BINDVAL(MAP_INT_STRING, std::map<int CYCLUS_COMMA std::string>);
312 CYCLUS_BINDVAL(MAP_STRING_INT, std::map<std::string CYCLUS_COMMA int>);
313 CYCLUS_BINDVAL(MAP_STRING_DOUBLE, std::map<std::string CYCLUS_COMMA double>);
315 std::map<std::string CYCLUS_COMMA std::string>);
317 std::map<std::string CYCLUS_COMMA std::vector<double> >);
320 std::map<std::string CYCLUS_COMMA std::map<int CYCLUS_COMMA double> >);
322 std::map<std::string CYCLUS_COMMA std::pair<
323 double CYCLUS_COMMA std::map<int CYCLUS_COMMA double> > >);
325 std::map<int CYCLUS_COMMA
326 std::map<std::string CYCLUS_COMMA double> >);
329 std::map<std::string CYCLUS_COMMA
330 std::vector<std::pair<int CYCLUS_COMMA
331 std::pair<std::string CYCLUS_COMMA std::string> > > >);
332
335 std::map<std::string CYCLUS_COMMA
336 std::pair<std::string CYCLUS_COMMA std::vector<double> > > );
337
338 CYCLUS_BINDVAL(LIST_PAIR_INT_INT, std::list< std::pair<int CYCLUS_COMMA int> >);
339
342 std::map<std::string CYCLUS_COMMA std::map<std::string CYCLUS_COMMA int> >);
343
346 std::vector<std::pair<
347 std::pair<double CYCLUS_COMMA double> CYCLUS_COMMA
348 std::map<std::string CYCLUS_COMMA double> > > );
349
352 std::map<std::pair<std::string CYCLUS_COMMA std::string> CYCLUS_COMMA int > );
353
356 std::map<std::string CYCLUS_COMMA std::map<std::string CYCLUS_COMMA double> >);
357
358 default: {
359 throw ValueError("attempted to retrieve unsupported sqlite backend type");
360 }
361 }
362#undef CYCLUS_BINDVAL
363#undef CYCLUS_COMMA
364}
365
367 int col,
368 DbTypes type) {
369
371
372// reconstructs from a serialization in stmt of type T and DbType D and
373// store it in v.
374#define CYCLUS_COMMA ,
375#define CYCLUS_LOADVAL(D, T) \
376 case D: { \
377 char* data = stmt->GetText(col, NULL); \
378 std::stringstream ss; \
379 ss << data; \
380 boost::archive::xml_iarchive ar(ss); \
381 T vect; \
382 ar & BOOST_SERIALIZATION_NVP(vect); \
383 v = vect; \
384 break; \
385 }
386
387 switch (type) {
388 case INT: {
389 v = stmt->GetInt(col);
390 break;
391 } case BOOL: {
392 v = static_cast<bool>(stmt->GetInt(col));
393 break;
394 } case DOUBLE: {
395 v = stmt->GetDouble(col);
396 break;
397 } case FLOAT: {
398 v = static_cast<float>(stmt->GetDouble(col));
399 break;
400 } case STRING: {
401 v = std::string(stmt->GetText(col, NULL));
402 break;
403 } case BLOB: {
404 int n;
405 char* s = stmt->GetText(col, &n);
406 v = Blob(std::string(s, n));
407 break;
408 } case UUID: {
409 boost::uuids::uuid u;
410 memcpy(&u, stmt->GetText(col, NULL), 16);
411 v = u;
412 break;
413 }
414 CYCLUS_LOADVAL(SET_INT, std::set<int>);
415 CYCLUS_LOADVAL(SET_STRING, std::set<std::string>);
416 CYCLUS_LOADVAL(LIST_INT, std::list<int>);
417 CYCLUS_LOADVAL(LIST_STRING, std::list<std::string>);
418 CYCLUS_LOADVAL(VECTOR_INT, std::vector<int>);
419 CYCLUS_LOADVAL(VECTOR_DOUBLE, std::vector<double>);
420 CYCLUS_LOADVAL(VECTOR_STRING, std::vector<std::string>);
421 CYCLUS_LOADVAL(MAP_INT_DOUBLE, std::map<int CYCLUS_COMMA double>);
422 CYCLUS_LOADVAL(MAP_INT_INT, std::map<int CYCLUS_COMMA int>);
423 CYCLUS_LOADVAL(MAP_INT_STRING, std::map<int CYCLUS_COMMA std::string>);
424 CYCLUS_LOADVAL(MAP_STRING_DOUBLE, std::map<std::string CYCLUS_COMMA double>);
425 CYCLUS_LOADVAL(MAP_STRING_INT, std::map<std::string CYCLUS_COMMA int>);
427 std::map<std::string CYCLUS_COMMA std::string>);
429 std::map<std::string CYCLUS_COMMA std::vector<double> >);
432 std::map<std::string CYCLUS_COMMA std::map<int CYCLUS_COMMA double> >);
434 std::map<std::string CYCLUS_COMMA std::pair<
435 double CYCLUS_COMMA std::map<int CYCLUS_COMMA double> > >);
437 std::map<int CYCLUS_COMMA
438 std::map<std::string CYCLUS_COMMA double> >);
441 std::map<std::string CYCLUS_COMMA
442 std::vector<std::pair<int CYCLUS_COMMA
443 std::pair<std::string CYCLUS_COMMA std::string> > > >);
444
447 std::map<std::string CYCLUS_COMMA
448 std::pair<std::string CYCLUS_COMMA std::vector<double> > > );
449
450 CYCLUS_LOADVAL(LIST_PAIR_INT_INT, std::list< std::pair<int CYCLUS_COMMA int> >);
451
454 std::map<std::string CYCLUS_COMMA std::map<std::string CYCLUS_COMMA int> >);
455
458 std::vector<std::pair<
459 std::pair<double CYCLUS_COMMA double> CYCLUS_COMMA
460 std::map<std::string CYCLUS_COMMA double> > > );
461
464 std::map<std::pair<std::string CYCLUS_COMMA std::string> CYCLUS_COMMA int > );
465
468 std::map<std::string CYCLUS_COMMA std::map<std::string CYCLUS_COMMA double> >);
469
470 default: {
471 throw ValueError("Attempted to retrieve unsupported backend type");
472 }}
473
474#undef CYCLUS_LOADVAL
475#undef CYCLUS_COMMA
476
477 return v;
478}
479
480std::string SqliteBack::SqlType(boost::spirit::hold_any v) {
481 switch (Type(v)) {
482 case INT: // fallthrough
483 case BOOL:
484 return "INTEGER";
485 case DOUBLE: // fallthrough
486 case FLOAT:
487 return "REAL";
488 case STRING:
489 return "TEXT";
490 case BLOB: // fallthrough
491 case UUID: // fallthrough
492 default: // all templated types
493 return "BLOB";
494 }
495}
496
497struct compare {
498 bool operator()(const std::type_info* a, const std::type_info* b) const {
499 return a->before(*b);
500 }
501};
502
503static std::map<const std::type_info*, DbTypes, compare> type_map;
504
505DbTypes SqliteBack::Type(boost::spirit::hold_any v) {
506 if (type_map.size() == 0) {
507 type_map[&typeid(int)] = INT;
508 type_map[&typeid(double)] = DOUBLE;
509 type_map[&typeid(float)] = FLOAT;
510 type_map[&typeid(bool)] = BOOL;
511 type_map[&typeid(Blob)] = BLOB;
512 type_map[&typeid(boost::uuids::uuid)] = UUID;
513 type_map[&typeid(std::string)] = STRING;
514 type_map[&typeid(std::set<int>)] = SET_INT;
515 type_map[&typeid(std::set<std::string>)] = SET_STRING;
516 type_map[&typeid(std::vector<int>)] = VECTOR_INT;
517 type_map[&typeid(std::vector<double>)] = VECTOR_DOUBLE;
518 type_map[&typeid(std::vector<std::string>)] = VECTOR_STRING;
519 type_map[&typeid(std::list<int>)] = LIST_INT;
520 type_map[&typeid(std::list<std::string>)] = LIST_STRING;
521 type_map[&typeid(std::map<int, int>)] = MAP_INT_INT;
522 type_map[&typeid(std::map<int, double>)] = MAP_INT_DOUBLE;
523 type_map[&typeid(std::map<int, std::string>)] = MAP_INT_STRING;
524 type_map[&typeid(std::map<std::string, int>)] = MAP_STRING_INT;
525 type_map[&typeid(std::map<std::string, double>)] = MAP_STRING_DOUBLE;
526 type_map[&typeid(std::map<std::string, std::string>)] = MAP_STRING_STRING;
527 type_map[&typeid(std::map<std::string, std::vector<double> >)] =
529 type_map[&typeid(std::map<std::string, std::map<int, double> >)] =
531 type_map[&typeid(std::map<std::string,
532 std::pair<double, std::map<int, double> > >)] =
534 type_map[&typeid(std::map<int, std::map<std::string, double> >)] =
536 type_map[&typeid(
537 std::map<std::string,
538 std::vector<std::pair<int, std::pair<std::string,
539 std::string> > > >)] =
541
542 type_map[&typeid(
543 std::map<std::string,
544 std::pair<std::string,
545 std::vector<double> > >)] =
547
548 type_map[&typeid(std::map<std::string, std::map<std::string,int> >)] =
550
551 type_map[&typeid(std::list<std::pair<int, int> >)] = LIST_PAIR_INT_INT;
552
553 type_map[&typeid(
554 std::vector<std::pair<std::pair<double, double>,
555 std::map<std::string, double> > > )] =
557
558 type_map[&typeid(
559 std::map<std::pair<std::string, std::string>, int > )] =
561
562 type_map[&typeid(std::map<std::string, std::map<std::string,double> >)] =
564 }
565
566 const std::type_info* ti = &v.type();
567 if (type_map.count(ti) == 0) {
568 throw ValueError(std::string("unsupported backend type ") + ti->name());
569 }
570 return type_map[ti];
571}
572
573} // namespace cyclus
A type to represent variable-length array of bytes for dumping to a cyclus output database.
Definition blob.h:9
Represents a condition used to filter rows returned by a query.
std::string field
table column name
Used to specify and send a collection of key-value pairs to the Recorder for recording.
Definition datum.h:15
std::vector< Entry > Vals
Definition datum.h:20
A generic mechanism to manually manage exceptions.
Definition error.h:12
Meta data and results of a query.
std::vector< std::string > fields
names of each field returned by a query
boost::shared_ptr< SqlStatement > Ptr
Definition sqlite_db.h:25
virtual ~SqliteBack()
virtual std::map< std::string, DbTypes > ColumnTypes(std::string table)
Return a map of column names of the specified table to the associated database type.
virtual void Notify(DatumList data)
Writes Datum objects immediately to the database as a single transaction.
virtual std::set< std::string > Tables()
Return a set of all table names currently in the database.
virtual QueryResult Query(std::string table, std::vector< Cond > *conds)
Return a set of rows from the specificed table that match all given conditions.
std::string Name()
Returns a unique name for this backend.
SqliteDb & db()
Returns the underlying sqlite database.
SqliteBack(std::string path)
Creates a new sqlite backend that will write to the database file specified by path.
void Flush()
Executes all pending commands.
An abstraction over the Sqlite native C interface to simplify database creation and data insertion.
Definition sqlite_db.h:77
void open()
Opens the sqlite database by either opening/creating a file (default) or creating/overwriting a file ...
Definition sqlite_db.cc:108
void close()
Finishes any incomplete operations and closes the database.
Definition sqlite_db.cc:99
void Execute(std::string cmd)
Execute an SQL command.
Definition sqlite_db.cc:139
SqlStatement::Ptr Prepare(std::string sql)
Creates a sqlite prepared statement for the given sql.
Definition sqlite_db.cc:133
For values that are too big, too small, etc.
Definition error.h:41
Code providing rudimentary logging capability for the Cyclus core.
#define CLOG(level)
Definition logger.h:39
taken directly from OsiSolverInterface.cpp on 2/17/14 from https://projects.coin-or....
Definition agent.cc:14
std::vector< std::string > split(const std::string &s, char delim)
std::vector< Datum * > DatumList
Definition rec_backend.h:12
@ LEV_ERROR
Use for errors that require agent code or input file modification (use extremely sparingly)
Definition logger.h:51
DbTypes
This is the primary list of all supported database types.
@ VECTOR_PAIR_PAIR_DOUBLE_DOUBLE_MAP_STRING_DOUBLE
@ MAP_STRING_STRING
@ MAP_STRING_MAP_STRING_DOUBLE
@ MAP_INT_MAP_STRING_DOUBLE
@ MAP_STRING_VECTOR_DOUBLE
@ MAP_STRING_VECTOR_PAIR_INT_PAIR_STRING_STRING
@ MAP_STRING_MAP_STRING_INT
@ LIST_PAIR_INT_INT
@ MAP_STRING_DOUBLE
@ MAP_PAIR_STRING_STRING_INT
@ MAP_STRING_PAIR_DOUBLE_MAP_INT_DOUBLE
@ MAP_STRING_PAIR_STRING_VECTOR_DOUBLE
@ VECTOR_STRING
@ VECTOR_DOUBLE
@ MAP_STRING_MAP_INT_DOUBLE
std::vector< boost::spirit::hold_any > QueryRow
static std::map< const std::type_info *, DbTypes, compare > type_map
T OptionalQuery(InfileTree *tree, std::string query, T default_val)
a query method for optional parameters
memcpy(dest, src, size)
std::string name(int nuc)
Definition pyne.cc:2940
#define CYCLUS_COMMA
#define CYCLUS_BINDVAL(D, T)
#define CYCLUS_LOADVAL(D, T)
Represents column information.
bool operator()(const std::type_info *a, const std::type_info *b) const