For instance, this code snipplet is taken from a test for SQL
with std::vector:
using container = std::vector<sample>; container data; sql::connection<container> db(data); TabSample tab; db.run(insert_into(tab).set(tab.alpha = 17)); db.run(insert_into(tab).set(tab.beta = "cheesecake")); db.run(insert_into(tab).set(tab.alpha = 42, tab.beta = "hello", tab.gamma = true)); db.run(insert_into(tab).set(tab.gamma = true)); for (const sample& row: db.run(select(all_of(tab(tab)) .from(tab) .where(tab.alpha < 18))) { std::cerr << "alpha=" << row.alpha << ", beta=" << row.beta << ", gamma=" << row.gamma << std::endl; }The syntax is the same as with any "normal" SQL database, but here the SQL statements are transformed into functors at compile time, which do the appropriate things at runtime.
There is no formal proposal yet, but I believe that an approach with strongly typed compile time expressions is superior to any string based approach.
On Sun, Feb 16, 2014 at 7:22 PM, Roland Bock <rb...@eudoxos.de> wrote:
There is no formal proposal yet, but I believe that an approach with strongly typed compile time expressions is superior to any string based approach.
I believe that without a proposal, not a lot more can be done by this group for now (other than giving feedback on the library, like on the boost mailing list).
Therefore, are you currently working on that proposal or not yet? Or maybe you are looking for someone to help writing one? (I'm not available but making it clear might help finding someone here).
A few questions/comments:
ad benefit 3.) This only helps against syntax errors in sql queries. It doesn't help against a lot of other problems which arise in combination with databases, at runtime (Missing tables, incorrect database schema versions, incorrect database versions, ...), and no untested sql statements should go into production code.
ad benefit 4.) This sounds interesting, if I unterstand this correct. Does this mean you are able to access any relational database without SQL ? Only using your compile time expression tree? How does this work?
In the latter cases, you skip the serialization/parsing steps
which improves runtime performance and lowers the barrier for
using SQL syntax with databases which do not have a string-based
SQL interface like containers or XML or JSON, etc.
Of course, someone has write libraries to perform this
translation of expression trees. This does not happen
automatically. But it is much simpler than I anticipated a few
months ago. I had a first version of the partial SQL-interface for
std::vector ready in about a day.
ad benefit 5.) There are no such things as "SQL databases", there are also no such things as "No SQL Databases". There are "relational Databases" for which SQL is used to communicate with them, and there are highly optimized (for speed) key/value stores which use other interfaces.
Question 1.) In your snippet, how are the types sample and TabSample defined?
struct sample { int64_t alpha; std::string beta; bool gamma; };The class TabSample describes this struct as a database table to the library sqlpp11. I attached the header file for TabSample. Most of it is boilerplate which could easily be generated by a simple macro or a small code generator.
Question 2.) Have plans to support any kind of SQL statements (e.g. Common Table Expressions),or only a limited subset?
The problem with Database abstractions at this level (like with entity frameworks) IMHO is, thatthe user/programmer of such libraries loose the understanding of a relational database and how touse it in a performant manner. So they would never replace SQL completely.
--
You received this message because you are subscribed to the Google Groups "SG11 - Databases" group.
To unsubscribe from this group and stop receiving emails from it, send an email to databases+...@isocpp.org.
To post to this group, send email to data...@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/databases/.
For more options, visit https://groups.google.com/a/isocpp.org/groups/opt_out.
The "ad benefit 3.)" comment is important: schema management is not optional.
For example the Scala/Lift Mapper has a "Schemifier.schemify" function.You can call this when your application starts - it will figure out the difference between the schema encoded in your app and the actual database.
It will emit the requires SQL commands if there's any difference.
So ideally in your C++ code you could just a add a new field to Users class, called "timestamp FirstContacted", build and run.The "schemifier" would take care of "ALTER TABLE users ADD COLUMN first_contacted timestamp" query.
This way you can have relationships. data types and widths, etc all in your code, no additional tool required.
Roland,
> Nice idea, it sounds cool but also bit scary
That's why you call the schemify function and it does not just happen automatically.
Of course moving fields and other advanced changes you still have to do manually - but that does not happen often.In past 3-4 years maybe we had 2 manual changes and hundreds of automatic updates."add new field x", "increase to string 50" etc are all automatic schema migrations.
High-level comments: in a similar vein to other feedback, and without downloading the code yet:
- I think there needs to be support for SQL dialects, represented as separate type collections (e.g., postgresql::statement, postgresql::database, sqlite::database, etc.). There may be a base dialect from which the others can be built (helps with reuse and reduces burden to add a dialect).
template<typename Database> using blank_select_t = statement_t<Database, select_t, no_select_flag_list_t, no_select_column_list_t, no_from_t, no_extra_tables_t, no_where_t<true>, no_group_by_t, no_having_t, no_order_by_t, no_limit_t, no_offset_t>;Behind the curtains, when you declare the columns you want to select, the no_select_column_list_t is replaced by an appropriate type, when you declare which tables you want to select from, the no_from_t is replaced, etc.
- If/when there's reflection and an externalized representation of column, table, view, etc. names, then the query expressions should also be constructed from them (e.g., insert<Reflection>(stmt, source) could be replaced by insert<Reflection>(database, source), where the statement could be created from a query expression, which is created from the Reflection).
- I'd be very interested in seeing a full DSL, akin to Boost Fusion, but I'm not sure that something like that could ever fly at the C++ Standards level.
- Does this work with expressions being deferred? E.g., does ".where(tab.alpha < 18)" result in an expression functor that renders "WHERE $1 < 18"?
.where(t.alpha == parameter(t.alpha))
auto s = select(all_of(t)).from(t).where(t.alpha == parameter(t.alpha)); auto p = db.prepare(s); p.params.alpha = 7; for (const auto& row : db(p)) { std::cout << row.alpha << std::endl; }Is that what you are looking for? Otherwise please explain in more detail :-)
- Ultimately it'd be nice to have the compiler generate the query test. I'm not sure that there's a sensible approach with constexpr (there have been lots of suggestions on text processing at compile-time, but they are messy).
On 2014-06-29 23:22, William La Cholter wrote:
That is one of the great potential benefits indeed. The expression tree can be translated directly into code understood by the back-end. I have not gotten to do that with any "classic" database yet, but I have a proof of concept for inserting/selecting structs to/from a vector [1]. Here the SQL expression tree is translated at compile time into a combination of functors that perform the insert/select.
I think a query builder could be a great thing. And if fused to a database back-end could enable code considerably faster than the current method of client: generate text -> client: bind values -> server: parse text -> server: extract values -> server execute query