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
22#include "blob.h"
23#include "datum.h"
24#include "error.h"
25#include "logger.h"
26
27namespace cyclus {
28
29std::vector<std::string> split(const std::string& s, char delim) {
30 std::vector<std::string> elems;
31 std::stringstream ss(s);
32 std::string item;
33 while (std::getline(ss, item, delim)) {
34 elems.push_back(item);
35 }
36 return elems;
37}
38
40 try {
41 Flush();
42 db_.close();
43 } catch (Error err) {
44 CLOG(LEV_ERROR) << "Error in SqliteBack destructor: " << err.what();
45 }
46}
47
48SqliteBack::SqliteBack(std::string path) : db_(path) {
49 path_ = path;
50 db_.open();
51
52 db_.Execute("PRAGMA synchronous=OFF;");
53 db_.Execute("PRAGMA journal_mode=MEMORY;");
54 db_.Execute("PRAGMA temp_store=MEMORY;");
55
56 // cache pre-existing table names
58 stmt = db_.Prepare("SELECT name FROM sqlite_master WHERE type='table';");
59
60 for (int i = 0; stmt->Step(); ++i) {
61 tbl_names_.insert(stmt->GetText(0, NULL));
62 }
63
64 if (tbl_names_.count("FieldTypes") == 0) {
65 std::string cmd = "CREATE TABLE IF NOT EXISTS FieldTypes";
66 cmd += "(TableName TEXT,Field TEXT,Type INTEGER);";
67 db_.Execute(cmd);
68 }
69}
70
72 db_.Execute("BEGIN TRANSACTION;");
73 try {
74 for (DatumList::iterator it = data.begin(); it != data.end(); ++it) {
75 std::string tbl = (*it)->title();
76 if (tbl_names_.count(tbl) == 0) {
77 CreateTable(*it);
78 }
79 if (stmts_.count(tbl) == 0) {
80 BuildStmt(*it);
81 }
82 WriteDatum(*it);
83 }
84 } catch (ValueError err) {
85 db_.Execute("END TRANSACTION;");
86 throw ValueError(err.what());
87 }
88 db_.Execute("END TRANSACTION;");
89 Flush();
90}
91
93
94std::list<ColumnInfo> SqliteBack::Schema(std::string table) {
95 std::list<ColumnInfo> schema;
96 QueryResult qr = GetTableInfo(table);
97 for (int i = 0; i < qr.fields.size(); ++i) {
98 ColumnInfo info = 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)
144 rtn[qr.fields[i]] = qr.types[i];
145 return rtn;
146}
147
148std::set<std::string> SqliteBack::Tables() {
149 using std::set;
150 using std::string;
151 set<string> rtn;
152 std::string sql = "SELECT name FROM sqlite_master WHERE type='table';";
154 stmt = db_.Prepare(sql);
155 while (stmt->Step()) {
156 rtn.insert(stmt->GetText(0, NULL));
157 }
158 rtn.erase("FieldTypes");
159 return rtn;
160}
161
163 return db_;
164}
165
166QueryResult SqliteBack::GetTableInfo(std::string table) {
167 std::string sql = "SELECT Field,Type FROM FieldTypes WHERE TableName = '" +
168 table + "';";
170 stmt = db_.Prepare(sql);
171
172 int i = 0;
173 QueryResult info;
174 for (i = 0; stmt->Step(); ++i) {
175 info.fields.push_back(stmt->GetText(0, NULL));
176 info.types.push_back((DbTypes)stmt->GetInt(1));
177 }
178 if (i == 0) {
179 throw ValueError("Invalid table name " + table);
180 }
181 return info;
182}
183
184std::string SqliteBack::Name() {
185 return path_;
186}
187
188void SqliteBack::BuildStmt(Datum* d) {
189 std::string name = d->title();
190 Datum::Vals vals = d->vals();
191 std::vector<DbTypes> schema;
192
193 schema.push_back(Type(vals[0].second));
194 std::string insert = "INSERT INTO " + name + " VALUES (?";
195 for (int i = 1; i < vals.size(); ++i) {
196 schema.push_back(Type(vals[i].second));
197 insert += ", ?";
198 }
199 insert += ");";
200
201 schemas_[name] = schema;
202 stmts_[name] = db_.Prepare(insert);
203}
204
205void SqliteBack::CreateTable(Datum* d) {
206 std::string name = d->title();
207 tbl_names_.insert(name);
208
209 Datum::Vals vals = d->vals();
210 Datum::Vals::iterator it = vals.begin();
211
212 std::stringstream types;
213 types << "INSERT INTO FieldTypes VALUES ('"
214 << name << "','" << it->first << "','"
215 << Type(it->second) << "');";
216 db_.Execute(types.str());
217
218 std::string cmd = "CREATE TABLE " + name + " (";
219 cmd += std::string(it->first) + " " + SqlType(it->second);
220 ++it;
221
222 while (it != vals.end()) {
223 cmd += ", " + std::string(it->first) + " " + SqlType(it->second);
224 std::stringstream types;
225 types << "INSERT INTO FieldTypes VALUES ('"
226 << name << "','" << it->first << "','"
227 << Type(it->second) << "');";
228 db_.Execute(types.str());
229 ++it;
230 }
231
232 cmd += ");";
233 db_.Execute(cmd);
234}
235
236void SqliteBack::WriteDatum(Datum* d) {
237 Datum::Vals vals = d->vals();
238 SqlStatement::Ptr stmt = stmts_[d->title()];
239 std::vector<DbTypes> schema = schemas_[d->title()];
240
241 for (int i = 0; i < vals.size(); ++i) {
242 boost::spirit::hold_any v = vals[i].second;
243 Bind(v, schema[i], stmt, i+1);
244 }
245
246 stmt->Exec();
247}
248
249void SqliteBack::Bind(boost::spirit::hold_any v, DbTypes type, SqlStatement::Ptr stmt,
250 int index) {
251
252// serializes the value v of type T and DBType D and binds it to stmt (inside
253// a case statement.
254// NOTE: Since we are archiving to a stringstream, the archive must be closed before
255// the stringstream, so we put it in its own scope. This first became an issue in
256// 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
257#define CYCLUS_COMMA ,
258#define CYCLUS_BINDVAL(D, T) \
259 case D: { \
260 T vect = v.cast<T>(); \
261 std::stringstream ss; \
262 ss.imbue(std::locale("")); \
263 { \
264 boost::archive::xml_oarchive ar(ss); \
265 ar & BOOST_SERIALIZATION_NVP(vect); \
266 } \
267 v = vect; \
268 std::string s = ss.str(); \
269 stmt->BindBlob(index, s.c_str(), s.size()); \
270 break; \
271 }
272
273 switch (type) {
274 case INT: {
275 stmt->BindInt(index, v.cast<int>());
276 break;
277 }
278 case BOOL: {
279 stmt->BindInt(index, v.cast<bool>());
280 break;
281 }
282 case DOUBLE: {
283 stmt->BindDouble(index, v.cast<double>());
284 break;
285 }
286 case FLOAT: {
287 stmt->BindDouble(index, v.cast<float>());
288 break;
289 }
290 case BLOB: {
291 std::string s = v.cast<Blob>().str();
292 stmt->BindBlob(index, s.c_str(), s.size());
293 break;
294 }
295 case STRING: {
296 stmt->BindText(index, v.cast<std::string>().c_str());
297 break;
298 }
299 case UUID: {
300 boost::uuids::uuid ui = v.cast<boost::uuids::uuid>();
301 stmt->BindBlob(index, ui.data, 16);
302 break;
303 }
304 CYCLUS_BINDVAL(SET_INT, std::set<int>);
305 CYCLUS_BINDVAL(SET_STRING, std::set<std::string>);
306 CYCLUS_BINDVAL(LIST_INT, std::list<int>);
307 CYCLUS_BINDVAL(LIST_STRING, std::list<std::string>);
308 CYCLUS_BINDVAL(VECTOR_INT, std::vector<int>);
309 CYCLUS_BINDVAL(VECTOR_DOUBLE, std::vector<double>);
310 CYCLUS_BINDVAL(VECTOR_STRING, std::vector<std::string>);
311 CYCLUS_BINDVAL(MAP_INT_DOUBLE, std::map<int CYCLUS_COMMA double>);
312 CYCLUS_BINDVAL(MAP_INT_INT, std::map<int CYCLUS_COMMA int>);
313 CYCLUS_BINDVAL(MAP_INT_STRING, std::map<int CYCLUS_COMMA std::string>);
314 CYCLUS_BINDVAL(MAP_STRING_INT, std::map<std::string CYCLUS_COMMA int>);
315 CYCLUS_BINDVAL(MAP_STRING_DOUBLE, std::map<std::string CYCLUS_COMMA double>);
317 std::map<std::string CYCLUS_COMMA std::string>);
319 std::map<std::string CYCLUS_COMMA std::vector<double> >);
322 std::map<std::string CYCLUS_COMMA std::map<int CYCLUS_COMMA double> >);
324 std::map<std::string CYCLUS_COMMA std::pair<
325 double CYCLUS_COMMA std::map<int CYCLUS_COMMA double> > >);
327 std::map<int CYCLUS_COMMA
328 std::map<std::string CYCLUS_COMMA double> >);
331 std::map<std::string CYCLUS_COMMA
332 std::vector<std::pair<int CYCLUS_COMMA
333 std::pair<std::string CYCLUS_COMMA std::string> > > >);
334
337 std::map<std::string CYCLUS_COMMA
338 std::pair<std::string CYCLUS_COMMA std::vector<double> > > );
339
340 CYCLUS_BINDVAL(LIST_PAIR_INT_INT, std::list< std::pair<int CYCLUS_COMMA int> >);
341
344 std::map<std::string CYCLUS_COMMA std::map<std::string CYCLUS_COMMA int> >);
345
348 std::vector<std::pair<
349 std::pair<double CYCLUS_COMMA double> CYCLUS_COMMA
350 std::map<std::string CYCLUS_COMMA double> > > );
351
354 std::map<std::pair<std::string CYCLUS_COMMA std::string> CYCLUS_COMMA int > );
355
358 std::map<std::string CYCLUS_COMMA std::map<std::string CYCLUS_COMMA double> >);
359
360 default: {
361 throw ValueError("attempted to retrieve unsupported sqlite backend type");
362 }
363 }
364#undef CYCLUS_BINDVAL
365#undef CYCLUS_COMMA
366}
367
368boost::spirit::hold_any SqliteBack::ColAsVal(SqlStatement::Ptr stmt,
369 int col,
370 DbTypes type) {
371
373
374// reconstructs from a serialization in stmt of type T and DbType D and
375// store it in v.
376#define CYCLUS_COMMA ,
377#define CYCLUS_LOADVAL(D, T) \
378 case D: { \
379 char* data = stmt->GetText(col, NULL); \
380 std::stringstream ss; \
381 ss.imbue(std::locale("")); \
382 ss << data; \
383 boost::archive::xml_iarchive ar(ss); \
384 T vect; \
385 ar & BOOST_SERIALIZATION_NVP(vect); \
386 v = vect; \
387 break; \
388 }
389
390 switch (type) {
391 case INT: {
392 v = stmt->GetInt(col);
393 break;
394 } case BOOL: {
395 v = static_cast<bool>(stmt->GetInt(col));
396 break;
397 } case DOUBLE: {
398 v = stmt->GetDouble(col);
399 break;
400 } case FLOAT: {
401 v = static_cast<float>(stmt->GetDouble(col));
402 break;
403 } case STRING: {
404 v = std::string(stmt->GetText(col, NULL));
405 break;
406 } case BLOB: {
407 int n;
408 char* s = stmt->GetText(col, &n);
409 v = Blob(std::string(s, n));
410 break;
411 } case UUID: {
412 boost::uuids::uuid u;
413 memcpy(&u, stmt->GetText(col, NULL), 16);
414 v = u;
415 break;
416 }
417 CYCLUS_LOADVAL(SET_INT, std::set<int>);
418 CYCLUS_LOADVAL(SET_STRING, std::set<std::string>);
419 CYCLUS_LOADVAL(LIST_INT, std::list<int>);
420 CYCLUS_LOADVAL(LIST_STRING, std::list<std::string>);
421 CYCLUS_LOADVAL(VECTOR_INT, std::vector<int>);
422 CYCLUS_LOADVAL(VECTOR_DOUBLE, std::vector<double>);
423 CYCLUS_LOADVAL(VECTOR_STRING, std::vector<std::string>);
424 CYCLUS_LOADVAL(MAP_INT_DOUBLE, std::map<int CYCLUS_COMMA double>);
425 CYCLUS_LOADVAL(MAP_INT_INT, std::map<int CYCLUS_COMMA int>);
426 CYCLUS_LOADVAL(MAP_INT_STRING, std::map<int CYCLUS_COMMA std::string>);
427 CYCLUS_LOADVAL(MAP_STRING_DOUBLE, std::map<std::string CYCLUS_COMMA double>);
428 CYCLUS_LOADVAL(MAP_STRING_INT, std::map<std::string CYCLUS_COMMA int>);
430 std::map<std::string CYCLUS_COMMA std::string>);
432 std::map<std::string CYCLUS_COMMA std::vector<double> >);
435 std::map<std::string CYCLUS_COMMA std::map<int CYCLUS_COMMA double> >);
437 std::map<std::string CYCLUS_COMMA std::pair<
438 double CYCLUS_COMMA std::map<int CYCLUS_COMMA double> > >);
440 std::map<int CYCLUS_COMMA
441 std::map<std::string CYCLUS_COMMA double> >);
444 std::map<std::string CYCLUS_COMMA
445 std::vector<std::pair<int CYCLUS_COMMA
446 std::pair<std::string CYCLUS_COMMA std::string> > > >);
447
450 std::map<std::string CYCLUS_COMMA
451 std::pair<std::string CYCLUS_COMMA std::vector<double> > > );
452
453 CYCLUS_LOADVAL(LIST_PAIR_INT_INT, std::list< std::pair<int CYCLUS_COMMA int> >);
454
457 std::map<std::string CYCLUS_COMMA std::map<std::string CYCLUS_COMMA int> >);
458
461 std::vector<std::pair<
462 std::pair<double CYCLUS_COMMA double> CYCLUS_COMMA
463 std::map<std::string CYCLUS_COMMA double> > > );
464
467 std::map<std::pair<std::string CYCLUS_COMMA std::string> CYCLUS_COMMA int > );
468
471 std::map<std::string CYCLUS_COMMA std::map<std::string CYCLUS_COMMA double> >);
472
473 default: {
474 throw ValueError("Attempted to retrieve unsupported backend type");
475 }}
476
477#undef CYCLUS_LOADVAL
478#undef CYCLUS_COMMA
479
480 return v;
481}
482
483std::string SqliteBack::SqlType(boost::spirit::hold_any v) {
484 switch (Type(v)) {
485 case INT: // fallthrough
486 case BOOL:
487 return "INTEGER";
488 case DOUBLE: // fallthrough
489 case FLOAT:
490 return "REAL";
491 case STRING:
492 return "TEXT";
493 case BLOB: // fallthrough
494 case UUID: // fallthrough
495 default: // all templated types
496 return "BLOB";
497 }
498}
499
500struct compare {
501 bool operator()(const std::type_info* a, const std::type_info* b) const {
502 return a->before(*b);
503 }
504};
505
506static std::map<const std::type_info*, DbTypes, compare> type_map;
507
508DbTypes SqliteBack::Type(boost::spirit::hold_any v) {
509 if (type_map.size() == 0) {
510 type_map[&typeid(int)] = INT;
511 type_map[&typeid(double)] = DOUBLE;
512 type_map[&typeid(float)] = FLOAT;
513 type_map[&typeid(bool)] = BOOL;
514 type_map[&typeid(Blob)] = BLOB;
515 type_map[&typeid(boost::uuids::uuid)] = UUID;
516 type_map[&typeid(std::string)] = STRING;
517 type_map[&typeid(std::set<int>)] = SET_INT;
518 type_map[&typeid(std::set<std::string>)] = SET_STRING;
519 type_map[&typeid(std::vector<int>)] = VECTOR_INT;
520 type_map[&typeid(std::vector<double>)] = VECTOR_DOUBLE;
521 type_map[&typeid(std::vector<std::string>)] = VECTOR_STRING;
522 type_map[&typeid(std::list<int>)] = LIST_INT;
523 type_map[&typeid(std::list<std::string>)] = LIST_STRING;
524 type_map[&typeid(std::map<int, int>)] = MAP_INT_INT;
525 type_map[&typeid(std::map<int, double>)] = MAP_INT_DOUBLE;
526 type_map[&typeid(std::map<int, std::string>)] = MAP_INT_STRING;
527 type_map[&typeid(std::map<std::string, int>)] = MAP_STRING_INT;
528 type_map[&typeid(std::map<std::string, double>)] = MAP_STRING_DOUBLE;
529 type_map[&typeid(std::map<std::string, std::string>)] = MAP_STRING_STRING;
530 type_map[&typeid(std::map<std::string, std::vector<double> >)] =
532 type_map[&typeid(std::map<std::string, std::map<int, double> >)] =
534 type_map[&typeid(std::map<std::string,
535 std::pair<double, std::map<int, double> > >)] =
537 type_map[&typeid(std::map<int, std::map<std::string, double> >)] =
539 type_map[&typeid(
540 std::map<std::string,
541 std::vector<std::pair<int, std::pair<std::string,
542 std::string> > > >)] =
544
545 type_map[&typeid(
546 std::map<std::string,
547 std::pair<std::string,
548 std::vector<double> > >)] =
550
551 type_map[&typeid(std::map<std::string, std::map<std::string,int> >)] =
553
554 type_map[&typeid(std::list<std::pair<int, int> >)] = LIST_PAIR_INT_INT;
555
556 type_map[&typeid(
557 std::vector<std::pair<std::pair<double, double>,
558 std::map<std::string, double> > > )] =
560
561 type_map[&typeid(
562 std::map<std::pair<std::string, std::string>, int > )] =
564
565 type_map[&typeid(std::map<std::string, std::map<std::string,double> >)] =
567 }
568
569 const std::type_info* ti = &v.type();
570 if (type_map.count(ti) == 0) {
571 throw ValueError(std::string("unsupported backend type ") + ti->name());
572 }
573 return type_map[ti];
574}
575
576} // namespace cyclus
T const & cast() const
Definition any.hpp:310
boost::core::typeinfo const & type() const
Definition any.hpp:304
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:52
const Vals & vals()
Returns a vector of all field-value pairs that have been added to this datum.
Definition datum.cc:56
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: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
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