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