Boost logo

Boost :

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


On 17/07/2014 10:48 PM, Roland Bock wrote:
> On 2014-07-17 13:11, Michael Shepanski wrote:
>> 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().
> Shouldn't collections be mentioned on that page, too?

I'm not sure I understand. Do you mean STL containers? Then no, these
are not mapped types (except for the special cases std::string and
std::vector<uint8_t>). You can't have a table<std::list<float>>.

>> 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.
> Hmm. I guess my question was unclear or I misunderstood you answer.
>
> I was thinking something like
>
> auto query = somethings.select(somethings->meta)
> if (userWantsBlob)
> query.add_to_select_list(somethings-blob);
> for(const auto& row : query)
> {
> if (userWantsBlob)
> {
> //somehow access blob here
> }
> }

Ah, I see. No, on two counts.

1. Quince doesn't let you modify a query. Instead, you can use an old
query as the basis for creating a new query, and the old query is unchanged.

2. q.select(...) lets you select values that are already part of q's
output, or computed from q's output, but if q is the query
"somethings.select(somethings->meta)", then q.select(...) will never be
able to get a blob out of it. (It wouldn't be compositional.)

> We have some code in our company that has several of these dynamic fields. Multiplying the code is not really an option then.

Iiuc you are starting with a query that gets something narrow, and then
using add_to_select_list() to widen it. With quince, you would work in
the opposite direction: start with a query that gets all the information
anyone needs (or just start with the table itself), and use select(...)
to make narrower queries as needed. Making new queries in this way is a
cheap operation.

Would that cause you any problems? (I'm not sure what you mean by
"Multiplying the code".)

>> 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?
> That's correct for queries with a static structure. For dynamic parts
> (like the optional blob in the example above), the compatibility would
> be tested when the dynamic part is added to the query.

Okay.

>> 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.)
> If you knew the database type while constructing the query, you could do
> those tests during construction. It probably wouldn't even be that hard
> with a similar mechanism as used in sqlpp11 (easy for me to say, still
> not knowing really much about your code).

As quince builds a query it always "knows" the database type in a
dynamic sense, but not in a static sense.

Cheers,
--- Michael


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