Boost logo

Boost :

Subject: Re: [boost] quince: queries in C++ expressions
From: Roland Bock (rbock_at_[hidden])
Date: 2014-07-17 03:09:23


On 2014-07-17 08:04, Michael Shepanski wrote:
> On 17/07/2014 2:48 PM, Roland Bock wrote:
>>
>> The automatic, ever changing alias is a cool feature. Since the actual
>> alias is unknown to the user, that requires the std::get<index> instead
>> of using a name, but that is probably OK for joins.
>
> What you say about std::get<index> makes me think there might be some
> residual misunderstanding.
>
> It's true that joins generate tuples. E.g. (from
> http://quince-lib.com/queries/join.html):
>
> const query<std::tuple<cinema, screen, movie>> combinations =
> join(cinemas, screens, movies);
>
> Code that receives the results would look like this:
>
> for (const std::tuple<cinema, screen, movie> &combo: combinations)
>
> As you can see, the members of the received tuple are a cinema, a
> screen, and a movie. So the user will say std::get<>(), but it's not
> to access individual columns.
>
> And in any case, if the user doesn't like tuples, he can use a
> collector class instead
> (http://quince-lib.com/queries/join/collector.html), then he doesn't
> need to use std::get<>() in any way at all.
It is really fascinating to see that many ideas are almost identical.
sqlpp11 also has a grouping some columns and give the group a name. It
is called multi_column, can be accessed by name just like columns and is
of course of great value in joins :-)
>
> All of these points apply the same to normal joins or self-joins. The
> only difference with self-joins is the use of table_alias objects in
> lieu of table objects.
Got it :-)

Does quince support adding columns at runtime, btw? Say, I have a table
that contains some meta data and a blob. I always want to select the
meta data, but whether or not to select the blob is decided at runtime.

>
>
>
>> Not exactly. In sqlpp11, the database type is only required for
>> serialization/interpretation. If the full statement structure is known
>> at compile time, the statement_t is independent of the database. Only if
>> you use parts which are decided at runtime (e.g. some expensive column X
>> is in the result row only if the user really, reallly wants it), then
>> these dynamic parts require to know the database and it is convenient to
>> do this by adding the database type to the complete statement.
>>
>> Thus, in at least 90% of the statements I write myself, the database
>> template parameter of the statement is just void.
>
> So, if it's not too much trouble, could you walk through the steps
> that lead to a compile-time failure, if I try to use a feature that is
> not supported on the DBMS where I am trying to use it?
Since you have watched the "selected template toffees" video, you have
seen all the tools already. Here's a walk through:

  * You have an expression tree and a context type. The latter is
    provided by the database
  * sqlpp11 provides a template function called serialize, which takes
    an generic expression tree and a generic context (basically any
    output stream).
  * sqlpp11 also provides "partial specializations" of these functions
    via a serializer template class. The partial specializations are
    available for all building blocks of the EDSL (e.g. tables, columns,
    select, where, arithmetic expressions, ...) and the generic context.
    You can see those at the bottom of each header file for such a
    building block, e.g.
    https://github.com/rbock/sqlpp11/blob/master/include/sqlpp11/group_by.h
  * If you need special behavior, you add a specialization for the
    respective building block and your own context type.
  * If your special behavior is that you do not support that feature,
    you just say so in a static_assert which fires when the serializer
    is instantiate

Here's an example from
https://github.com/rbock/sqlpp11-connector-sqlite3/blob/master/include/sqlpp11/sqlite3/serializer.h
(sqlite does not support full outer join):

template<typename Lhs, typename Rhs, typename On>
struct serializer_t<sqlite3::serializer_t, join_t<outer_join_t, Lhs,
Rhs, On>>
{
using T = join_t<outer_join_t, Lhs, Rhs, On>;
static void _(const T& t, sqlite3::serializer_t& context)
{
static_assert(::sqlpp::wrong_t<outer_join_t, Lhs, Rhs, On>::value, "No
support for outer join");
}
};

>
>
>> Yes, the connector library is completely free, but it does not have to
>> re-invent the wheel, too.
>>
>> sqlpp11 has a default serializer implementation which generates standard
>> SQL. The connector libraries only have to specialize for the things that
>> are actually special. For instance, MySQL uses the concat function
>> instead of the || operator. Thus it has to specialize string
>> concatenation.
>>
>> This is the full serializer specialization for the MySQL connector:
>> https://github.com/rbock/sqlpp11-connector-mysql/blob/master/include/sqlpp11/mysql/serializer.h
>>
>>
>> sqlpp11's EDSL is not complete yet, so there probably will be a few more
>> entries for other special things, for instance iirc, MySQL does not
>> support WITH, so I would disable it in that file by adding a static
>> assert into a specialization for with_t.
>
> Cool. (Btw I saw your online video about "template toffees", and it
> made me think I should do more with static_assert. My thinking has
> been "Any error I can catch at compile-time rather than run-time is
> awesome", but of course earlier is better, even at compile time.)

Thanks for the feedback :-)

Cheers,

Roland


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk