Hmm, that is a good question. I've cross-posted this to
comp.object (although I wonder if comp.software-eng would have
been appropriate), perhaps somebody there has seen some
research. Apologies if this is off-topic in comp.object; it
seems relevant, though.
Personally, I don't know of any such papers. There is more
than one way to do it. Take a look at SQLAPI++ for example
(
http://www.sqlapi.com/) (note for comp.object -- the original
post cited RogueWave), it does things in a slightly different
way.
I don't know of any general papers either, but each
implementation should be documented. I've used OTL in the
past, and it's concept is considerably different than that of
RogueWave.
I'm not sure, however, whether the OP was asking about such
concepts, or whether he was more interested about the mechanisms
behind them, e.g. how one would go about implementing something
like the RogueWave example he posted. While the obvious key for
this is operator overloading, I'm not aware of any papers which
really explain the fundamentals of creating a new language using
it. The trick here, of course, is that RWDBTable:
perator[]
is overloaded to return a special type, and all of the other
operators in the expression are overloaded on that type; an
operator== on the type doesn't do a comparison, but links in the
necessary information to generate the SQL command. And the
RWDBDeleter::where function takes a reference to the type,
follows the links, and uses the necessary information to build
the SQL command.
I'm not too sure I like the concept. I'm not really happy with
the idea that == doesn't do a comparison, and that && doesn't
short circuit; in other words, that `` purchases[ "videoID" ] ==
videoID '' can't be used as a condition, and that the second
operand of && will always be evaluated. Carefully designed,
however, it's possible to ensure that such an expression can
only be used as an argument to something like
RWDBDeleter::where, so it might not be as bad as that.
The above example is how the C++ roguewave library does it.
Assuming you had Table, Column, Deleter, and Where classes
what interface on those classes (and why) could generate the
std::string
where videoID==123 and last_name='Miller';
from the C++ code
deleter.where(purchases["videoID"] == videoID && purchases
["last_name"]==lastName);
I have seen APIs that do it similar to this (just an example):
deleteQuery
.from("mytable")
.constrain("videoID", videoID)
.constrain("last_name", lastName)
.execute();
In a language like C++ that allows operator overloading, you
could theoretically come up with a query builder based on
operator overloading:
That's exactly what the RogueWave interface is doing.
Query q = Delete << (Where("videoID") == videoID && Where
("last_name") == lastName);
q.Execute();
That is somewhat ugly but I did not put much thought into the
interface there. There, "Delete" and "Where" would be some
objects that are combined with the use of operators and, when
assigned to a Query, construct a representative SQL query.
The trick in the RogueWave implementation seems to be having
RWDBTable:
perator[] return a special type, on which the other
operators are defined. There are several ways to do this; I'd
probably use an expression node base class, with each of the
nodes derived classes. So ``purchases[ "videoID" ]'' might
return a ColumnReferenceNode (which contains the string
"videoID", and derived from ExpressionNode), there will be an
IntConstantNode with a converting constructor from int, and an
operator==( ExpressionNode const&, ExpressionNode const& ) which
returns an IsEqualNode containing pointers to the two nodes.
The RWDBDeleter::where function would take an ExpressionNode,
and "execute" it to generate the string.
The same thing could also be done with expression templates. I
find the virtual function version easier to understand, however.