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#include <locale>
6
7#include <boost/lexical_cast.hpp>
8#include <boost/uuid/uuid_io.hpp>
9#include <boost/algorithm/string.hpp>
10#include <boost/archive/tmpdir.hpp>
11#include <boost/archive/xml_iarchive.hpp>
12#include <boost/archive/xml_oarchive.hpp>
13#include <boost/serialization/base_object.hpp>
14#include <boost/serialization/utility.hpp>
15#include <boost/serialization/list.hpp>
16#include <boost/serialization/set.hpp>
17#include <boost/serialization/vector.hpp>
18#include <boost/serialization/map.hpp>
19#include <boost/serialization/assume_abstract.hpp>
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 =
98 ColumnInfo(table, qr.fields[i], i, qr.types[i], std::vector<int>());
99 schema.push_back(info);
100 }
101 return schema;
102}
103
104QueryResult SqliteBack::Query(std::string table, std::vector<Cond>* conds) {
105 QueryResult q = GetTableInfo(table);
106
107 std::stringstream sql;
108 sql << "SELECT * FROM " << table;
109 if (conds != NULL) {
110 sql << " WHERE ";
111 for (int i = 0; i < conds->size(); ++i) {
112 if (i > 0) {
113 sql << " AND ";
114 }
115 Cond c = (*conds)[i];
116 sql << c.field << " " << c.op << " ?";
117 }
118 }
119 sql << ";";
120
121 SqlStatement::Ptr stmt = db_.Prepare(sql.str());
122
123 if (conds != NULL) {
124 for (int i = 0; i < conds->size(); ++i) {
125 boost::spirit::hold_any v = (*conds)[i].val;
126 Bind(v, Type(v), stmt, i + 1);
127 }
128 }
129
130 for (int i = 0; stmt->Step(); ++i) {
131 QueryRow r;
132 for (int j = 0; j < q.fields.size(); ++j) {
133 r.push_back(ColAsVal(stmt, j, q.types[j]));
134 }
135 q.rows.push_back(r);
136 }
137 return q;
138}
139
140std::map<std::string, DbTypes> SqliteBack::ColumnTypes(std::string table) {
141 QueryResult qr = GetTableInfo(table);
142 std::map<std::string, DbTypes> rtn;
143 for (int i = 0; i < qr.fields.size(); ++i) 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;
150 set<string> rtn;
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 =
167 "SELECT Field,Type FROM FieldTypes WHERE TableName = '" + table + "';";
169 stmt = db_.Prepare(sql);
170
171 int i = 0;
172 QueryResult info;
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 ('" << name << "','" << it->first
213 << "','" << Type(it->second) << "');";
214 db_.Execute(types.str());
215
216 std::string cmd = "CREATE TABLE " + name + " (";
217 cmd += std::string(it->first) + " " + SqlType(it->second);
218 ++it;
219
220 while (it != vals.end()) {
221 cmd += ", " + std::string(it->first) + " " + SqlType(it->second);
222 std::stringstream types;
223 types << "INSERT INTO FieldTypes VALUES ('" << name << "','" << it->first
224 << "','" << Type(it->second) << "');";
225 db_.Execute(types.str());
226 ++it;
227 }
228
229 cmd += ");";
230 db_.Execute(cmd);
231}
232
233void SqliteBack::WriteDatum(Datum* d) {
234 Datum::Vals vals = d->vals();
235 SqlStatement::Ptr stmt = stmts_[d->title()];
236 std::vector<DbTypes> schema = schemas_[d->title()];
237
238 for (int i = 0; i < vals.size(); ++i) {
239 boost::spirit::hold_any v = vals[i].second;
240 Bind(v, schema[i], stmt, i + 1);
241 }
242
243 stmt->Exec();
244}
245
246void SqliteBack::Bind(boost::spirit::hold_any v, DbTypes type,
247 SqlStatement::Ptr stmt, int index) {
248// serializes the value v of type T and DBType D and binds it to stmt (inside
249// a case statement.
250// NOTE: Since we are archiving to a stringstream, the archive must be closed
251// before the stringstream, so we put it in its own scope. This first became an
252// issue in Boost v1.66.0. For more information, see
253// http://boost.2283326.n4.nabble.com/the-boost-xml-serialization-to-a-stringstream-does-not-have-an-end-tag-tp2580772p2580773.html
254#define CYCLUS_COMMA ,
255#define CYCLUS_BINDVAL(D, T) \
256 case D: { \
257 T vect = v.cast<T>(); \
258 std::stringstream ss; \
259 ss.imbue(std::locale("")); \
260 { \
261 boost::archive::xml_oarchive ar(ss); \
262 ar& BOOST_SERIALIZATION_NVP(vect); \
263 } \
264 v = vect; \
265 std::string s = ss.str(); \
266 stmt->BindBlob(index, s.c_str(), s.size()); \
267 break; \
268 }
269
270 switch (type) {
271 case INT: {
272 stmt->BindInt(index, v.cast<int>());
273 break;
274 }
275 case BOOL: {
276 stmt->BindInt(index, v.cast<bool>());
277 break;
278 }
279 case DOUBLE: {
280 stmt->BindDouble(index, v.cast<double>());
281 break;
282 }
283 case FLOAT: {
284 stmt->BindDouble(index, v.cast<float>());
285 break;
286 }
287 case BLOB: {
288 std::string s = v.cast<Blob>().str();
289 stmt->BindBlob(index, s.c_str(), s.size());
290 break;
291 }
292 case STRING: {
293 stmt->BindText(index, v.cast<std::string>().c_str());
294 break;
295 }
296 case UUID: {
297 boost::uuids::uuid ui = v.cast<boost::uuids::uuid>();
298 stmt->BindBlob(index, ui.data, 16);
299 break;
300 }
301 CYCLUS_BINDVAL(SET_INT, std::set<int>);
302 CYCLUS_BINDVAL(SET_STRING, std::set<std::string>);
303 CYCLUS_BINDVAL(LIST_INT, std::list<int>);
304 CYCLUS_BINDVAL(LIST_STRING, std::list<std::string>);
305 CYCLUS_BINDVAL(VECTOR_INT, std::vector<int>);
306 CYCLUS_BINDVAL(VECTOR_DOUBLE, std::vector<double>);
307 CYCLUS_BINDVAL(VECTOR_STRING, std::vector<std::string>);
308 CYCLUS_BINDVAL(MAP_INT_DOUBLE, std::map<int CYCLUS_COMMA double>);
309 CYCLUS_BINDVAL(MAP_INT_INT, std::map<int CYCLUS_COMMA int>);
310 CYCLUS_BINDVAL(MAP_INT_STRING, std::map<int CYCLUS_COMMA std::string>);
311 CYCLUS_BINDVAL(MAP_STRING_INT, std::map<std::string CYCLUS_COMMA int>);
313 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>>);
323 std::map<std::string CYCLUS_COMMA std::pair<
324 double CYCLUS_COMMA std::map<int CYCLUS_COMMA double>>>);
327 std::map<int CYCLUS_COMMA std::map<std::string CYCLUS_COMMA double>>);
330 std::map<std::string CYCLUS_COMMA
331 std::vector<std::pair<int CYCLUS_COMMA std::pair<
332 std::string CYCLUS_COMMA std::string>>>>);
333
335 std::map<std::string CYCLUS_COMMA std::pair<
336 std::string CYCLUS_COMMA std::vector<double>>>);
337
339 std::list<std::pair<int CYCLUS_COMMA int>>);
340
342 std::map<std::string CYCLUS_COMMA
343 std::map<std::string CYCLUS_COMMA int>>);
344
347 std::vector<
348 std::pair<std::pair<double CYCLUS_COMMA double> CYCLUS_COMMA
349 std::map<std::string CYCLUS_COMMA double>>>);
350
352 std::map<std::pair<std::string CYCLUS_COMMA std::string>
353 CYCLUS_COMMA int>);
354
356 std::map<std::string CYCLUS_COMMA
357 std::map<std::string CYCLUS_COMMA double>>);
358
359 default: {
360 throw ValueError("attempted to retrieve unsupported sqlite backend type");
361 }
362 }
363#undef CYCLUS_BINDVAL
364#undef CYCLUS_COMMA
365}
366
367boost::spirit::hold_any SqliteBack::ColAsVal(SqlStatement::Ptr stmt,
368 int col,
369 DbTypes type) {
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.imbue(std::locale("")); \
380 ss << data; \
381 boost::archive::xml_iarchive ar(ss); \
382 T vect; \
383 ar& BOOST_SERIALIZATION_NVP(vect); \
384 v = vect; \
385 break; \
386 }
387
388 switch (type) {
389 case INT: {
390 v = stmt->GetInt(col);
391 break;
392 }
393 case BOOL: {
394 v = static_cast<bool>(stmt->GetInt(col));
395 break;
396 }
397 case DOUBLE: {
398 v = stmt->GetDouble(col);
399 break;
400 }
401 case FLOAT: {
402 v = static_cast<float>(stmt->GetDouble(col));
403 break;
404 }
405 case STRING: {
406 v = std::string(stmt->GetText(col, NULL));
407 break;
408 }
409 case BLOB: {
410 int n;
411 char* s = stmt->GetText(col, &n);
412 v = Blob(std::string(s, n));
413 break;
414 }
415 case UUID: {
416 boost::uuids::uuid u;
417 memcpy(&u, stmt->GetText(col, NULL), 16);
418 v = u;
419 break;
420 }
421 CYCLUS_LOADVAL(SET_INT, std::set<int>);
422 CYCLUS_LOADVAL(SET_STRING, std::set<std::string>);
423 CYCLUS_LOADVAL(LIST_INT, std::list<int>);
424 CYCLUS_LOADVAL(LIST_STRING, std::list<std::string>);
425 CYCLUS_LOADVAL(VECTOR_INT, std::vector<int>);
426 CYCLUS_LOADVAL(VECTOR_DOUBLE, std::vector<double>);
427 CYCLUS_LOADVAL(VECTOR_STRING, std::vector<std::string>);
428 CYCLUS_LOADVAL(MAP_INT_DOUBLE, std::map<int CYCLUS_COMMA double>);
429 CYCLUS_LOADVAL(MAP_INT_INT, std::map<int CYCLUS_COMMA int>);
430 CYCLUS_LOADVAL(MAP_INT_STRING, std::map<int CYCLUS_COMMA std::string>);
432 std::map<std::string CYCLUS_COMMA double>);
433 CYCLUS_LOADVAL(MAP_STRING_INT, std::map<std::string CYCLUS_COMMA int>);
435 std::map<std::string CYCLUS_COMMA std::string>);
437 std::map<std::string CYCLUS_COMMA std::vector<double>>);
440 std::map<std::string CYCLUS_COMMA std::map<int CYCLUS_COMMA double>>);
443 std::map<std::string CYCLUS_COMMA std::pair<
444 double CYCLUS_COMMA std::map<int CYCLUS_COMMA double>>>);
447 std::map<int CYCLUS_COMMA std::map<std::string CYCLUS_COMMA double>>);
450 std::map<std::string CYCLUS_COMMA
451 std::vector<std::pair<int CYCLUS_COMMA std::pair<
452 std::string CYCLUS_COMMA std::string>>>>);
453
455 std::map<std::string CYCLUS_COMMA std::pair<
456 std::string CYCLUS_COMMA std::vector<double>>>);
457
459 std::list<std::pair<int CYCLUS_COMMA int>>);
460
462 std::map<std::string CYCLUS_COMMA
463 std::map<std::string CYCLUS_COMMA int>>);
464
467 std::vector<
468 std::pair<std::pair<double CYCLUS_COMMA double> CYCLUS_COMMA
469 std::map<std::string CYCLUS_COMMA double>>>);
470
472 std::map<std::pair<std::string CYCLUS_COMMA std::string>
473 CYCLUS_COMMA int>);
474
476 std::map<std::string CYCLUS_COMMA
477 std::map<std::string CYCLUS_COMMA double>>);
478
479 default: {
480 throw ValueError("Attempted to retrieve unsupported backend type");
481 }
482 }
483
484#undef CYCLUS_LOADVAL
485#undef CYCLUS_COMMA
486
487 return v;
488}
489
490std::string SqliteBack::SqlType(boost::spirit::hold_any v) {
491 switch (Type(v)) {
492 case INT: // fallthrough
493 case BOOL:
494 return "INTEGER";
495 case DOUBLE: // fallthrough
496 case FLOAT:
497 return "REAL";
498 case STRING:
499 return "TEXT";
500 case BLOB: // fallthrough
501 case UUID: // fallthrough
502 default: // all templated types
503 return "BLOB";
504 }
505}
506
507struct compare {
508 bool operator()(const std::type_info* a, const std::type_info* b) const {
509 return a->before(*b);
510 }
511};
512
513static std::map<const std::type_info*, DbTypes, compare> type_map;
514
515DbTypes SqliteBack::Type(boost::spirit::hold_any v) {
516 if (type_map.size() == 0) {
517 type_map[&typeid(int)] = INT;
518 type_map[&typeid(double)] = DOUBLE;
519 type_map[&typeid(float)] = FLOAT;
520 type_map[&typeid(bool)] = BOOL;
521 type_map[&typeid(Blob)] = BLOB;
522 type_map[&typeid(boost::uuids::uuid)] = UUID;
523 type_map[&typeid(std::string)] = STRING;
524 type_map[&typeid(std::set<int>)] = SET_INT;
525 type_map[&typeid(std::set<std::string>)] = SET_STRING;
526 type_map[&typeid(std::vector<int>)] = VECTOR_INT;
527 type_map[&typeid(std::vector<double>)] = VECTOR_DOUBLE;
528 type_map[&typeid(std::vector<std::string>)] = VECTOR_STRING;
529 type_map[&typeid(std::list<int>)] = LIST_INT;
530 type_map[&typeid(std::list<std::string>)] = LIST_STRING;
531 type_map[&typeid(std::map<int, int>)] = MAP_INT_INT;
532 type_map[&typeid(std::map<int, double>)] = MAP_INT_DOUBLE;
533 type_map[&typeid(std::map<int, std::string>)] = MAP_INT_STRING;
534 type_map[&typeid(std::map<std::string, int>)] = MAP_STRING_INT;
535 type_map[&typeid(std::map<std::string, double>)] = MAP_STRING_DOUBLE;
536 type_map[&typeid(std::map<std::string, std::string>)] = MAP_STRING_STRING;
537 type_map[&typeid(std::map<std::string, std::vector<double>>)] =
539 type_map[&typeid(std::map<std::string, std::map<int, double>>)] =
541 type_map[&typeid(
542 std::map<std::string, std::pair<double, std::map<int, double>>>)] =
544 type_map[&typeid(std::map<int, std::map<std::string, double>>)] =
546 type_map[&typeid(
547 std::map<std::string,
548 std::vector<
549 std::pair<int, std::pair<std::string, std::string>>>>)] =
551
552 type_map[&typeid(
553 std::map<std::string, std::pair<std::string, std::vector<double>>>)] =
555
556 type_map[&typeid(std::map<std::string, std::map<std::string, int>>)] =
558
559 type_map[&typeid(std::list<std::pair<int, int>>)] = LIST_PAIR_INT_INT;
560
561 type_map[&typeid(std::vector<std::pair<std::pair<double, double>,
562 std::map<std::string, double>>>)] =
564
565 type_map[&typeid(std::map<std::pair<std::string, std::string>, int>)] =
567
568 type_map[&typeid(std::map<std::string, std::map<std::string, double>>)] =
570 }
571
572 const std::type_info* ti = &v.type();
573 if (type_map.count(ti) == 0) {
574 throw ValueError(std::string("unsupported backend type ") + ti->name());
575 }
576 return type_map[ti];
577}
578
579} // namespace cyclus
T const & cast() const
Definition any.hpp:252
boost::core::typeinfo const & type() const
Definition any.hpp:250
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 op
One of: "<", ">", "<=", ">=", "==", "!=".
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
std::string title()
Returns the datum's title as specified during the datum's creation.
Definition datum.cc:51
const Vals & vals()
Returns a vector of all field-value pairs that have been added to this datum.
Definition datum.cc:55
A generic mechanism to manually manage exceptions.
Definition error.h:12
virtual const char * what() const
Returns the error message associated with this Error.
Definition error.cc:9
Meta data and results of a query.
std::vector< QueryRow > rows
ordered results of a query
std::vector< DbTypes > types
types of each field returned by a query.
std::vector< std::string > fields
names of each field returned by a query
boost::shared_ptr< SqlStatement > Ptr
Definition sqlite_db.h:26
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:78
SqlStatement::Ptr Prepare(std::string sql)
Creates a sqlite prepared statement for the given sql.
Definition sqlite_db.cc:131
For values that are too big, too small, etc.
Definition error.h:37
Code providing rudimentary logging capability for the Cyclus core.
#define CLOG(level)
Definition logger.h:40
basic_hold_any< char > hold_any
Definition any.hpp:345
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:56
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
memcpy(dest, src, size)
std::string name(int nuc)
Definition pyne.cc:1239
#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