[odb-users] Programmatically generating ODB queries

Oded Arbel oded at geek.co.il
Sat Sep 1 17:39:57 EDT 2012


Hi.

I'm trying to solve an issue where I'm support an API where users can
submit arbitrary queries (through an API called WBEM where users are
allowed to send "WQL" queries). The logical issue here is that the
query assumes as specific object structure which may or may not map
directly to the database entities - so I need to run some code to
parse and validate the input, then generate the database query.

To implement this I need:
1) something that parses the WQL into an abstract syntax tree
(obviously only a limited set of query operations is supported)
2) a map between "WQL names" to "database names", something like (
"WQL_Employee" => model::Employee ) => ( "WQL_first_name" =>
model::Employee::first_name, "WQL_last_name" => model::last_name, ...)
3) code that travels the AST and create an odb::query<Employee> object
which represents the the WQL text that the user had input

I have (1) working fine, and I'm having a bit of trouble with 2 and 3
because I'm finding it hard to do something like reflection on the ODB
types.

Currently for (2) I have something like this (the actual code is a lot
more complicated and uses macros and templates to be generic and easy
to write):
typedef boost::variant<odb::query<Employee>::first_name,
odb::query::<Employee>::last_name, [...] > validColumnType
typedef std::map<std::wstring, validColumnType> maptype;
maptype validFields();
validFields.insert(maptype::value_type("first",odb::query<Employee>::first_name));
validFields.insert(maptype::value_type("last",odb::query<Employee>::last_name));

And for (3) O have something like this (again, judicious use of
templates makes this sane for more then one model class):
odb::query<Employee> getQuery(const Expression& expression) const {
  if (expression.complex)
    return createComplexExpression(getQuery(expression.left()),
expression.op(), expression.right());
  Field left = expression.left();
  maptype::const_iterator column = validFields.find(left.getName());
  if (column == _fields.end())
    throw std::exception(("Invalid WQL field name");
  validColumnType col = column->second;
  return col.column() + expression.op() +
odb::query<ModelType>::_val(expression.right());
}
where right() is a constant value, such as int, op() returns a string
with the requested SQL operator and col is expected to be an
odb::query<ModelClass>::field that represents the field to be tested
against. (actually, boost::variant doesn't work as the last line would
have you believe and there is some "static_visitor" magic needed to
make that work, but operationally it behaves like the last line would
have worked if C++ had run-time type information).

So - is this completely crazy and there is a simple way to go about this?
-- 
Oded



More information about the odb-users mailing list