Boost logo

Boost :

Subject: Re: [boost] quince: queries in C++ expressions
From: Michael Shepanski (mps_at_[hidden])
Date: 2014-07-17 07:11:47


On 17/07/2014 5:09 PM, Roland Bock wrote:
> 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 :-)

The difference, of course, is that in quince we don't speak of "columns"
so much. :)

Every query produces values of its "value type", which can be any type
in this taxonomy:
http://quince-lib.com/mapped_data_types.html#mapped_data_types.definition_taxonomy
. It is common for a query's value type to be the same as a table's
value type, but this is not the case for queries that use join() or
select().

>> 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.

Sure. Quince's select() is analogous to supplying an SQL "select list".
(I say "analogous", not identical, because here again we work at the
level of C++ types, without regard to whether they are single-column or
multi-column.)

So it's:

     struct something {
         metadata meta; // metadata is some mapped type defined elsewhere
         vector<uint8_t> blob;
         string other;
     };
     QUINCE_MAP_CLASS(something, (meta)(blob)(other))

     extern table<something> somethings;

     // retrieve metadata only:
     for (const metadata &m: somethings.select(somethings->meta))
          // ...

     // retrieve all three parts:
     for (const something &s: somethings)
         // ...

     // retrieve metadata and blob only:
     for (const std::tuple<meta, vector<uint8_t>> &pair:
somethings.select(somethings->meta, somethings->blob))
         // ...

In the last example, we could use a collector class if we don't like tuples.

> 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");
> }
> };

Okay, I think I understand. When you have a complex expression tree
that includes, say, a full join somewhere deep inside, then that affects
the static type of the expression tree as a whole. Therefore, when you
ask a connector to serialize the tree, the compiler can see the
incompatibility and fail. Yes?

If so, then it confirms what I suspected (albeit for different reasons):
I can't make quince behave this way -- or at least the price of doing so
would be very high. A complex quince::query object may have a full join
buried somewhere inside, but it doesn't affect the object's static type.
(It only becomes apparent when quince tries to generate the SQL, so it
traverses the tree, calling virtual methods, etc.)

Cheers,
--- Michael


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