Boost logo

Boost :

Subject: Re: [boost] SQL client library ?
From: Christoph Heindl (christoph.heindl_at_[hidden])
Date: 2009-09-06 05:36:37


Hi,

I've been working on an type-safe sql interface geared towards sqlite
(and actually based on sqlitepp). Below you find a usage example. If
anyone is interested I'll glad to share the source (once I have
clarifed copyrights with the company I work for).

Best regards,
Christoph

using namespace ::Examples::LibDb;

    /// @brief Introductionary example for ::LibDb
    BOOST_AUTO_TEST_CASE(oneminute)
    {
      try {
        // A database connection is required. This is provided by
::LibDb::SQL::Session. We open a database in memory. Alternatively
        // you can specify a file to be openend/created.
        ::LibDb::SQL::Session session(":memory:");

        // To access a database, read-only or read/write, we need to
tell the library what entities we want to deal with.
        // Table entities are modelled as classes with public column
attributes. The entities for this example are modelled in the
        // namespace DatabaseEntities. To interact with tables we need
instances of the classes that modell the entity.
        // As interaction with classes is mostly immutable you are
safe to query a table instance from the singleton
        // table factory.
        const DatabaseEntities::Authors &authors =
::LibDb::Entity::Schema::table<DatabaseEntities::Authors>();
        const DatabaseEntities::Books &books =
::LibDb::Entity::Schema::table<DatabaseEntities::Books>();

        // As our database is currently empty we proceed to create our
tables in the database.
        ::LibDb::SQL::create_table(authors).execute(session);
        ::LibDb::SQL::create_table(books).execute(session);

        // On error the library throws std::runtime_error if a SQLite
error occurred or
        // std::bad_exception.

        // Inserting records into the database is simple: Values can
be assigned to columns (public attributes of our entity classes).
        // The column itself does not store the value assigned, but
rather generates a pair of column and value and methods to concatenate
        // other column/value pairs. Assignment of values to columns
is typesafe.
        ::LibDb::SQL::insert_into(authors, (authors.first_name =
"Agatha", authors.last_name = "Christie")).execute(session);
        ::LibDb::SQL::insert_into(authors, (authors.first_name =
"Lew", authors.last_name = "Tolstoi")).execute(session);

        // To insert books for Agatha we need to know Agatha's
generated primaray key id. This is accomplished by selection
        // The columns you want to select are specified along with
variables that receive the selected values (typesafe).
        // Additionally the result-set is narrowed down by only
selecting those records where first name column corresponds
        // to Agatha. The column values of the first row are
immediately assigned to the provided mutable variables.
        long long agatha_id, lew_id;
        ::LibDb::SQL::select(authors.id >>
agatha_id).where(authors.first_name == "Agatha").execute(session);
        ::LibDb::SQL::select(authors.id >>
lew_id).where(authors.first_name == "Lew").execute(session);

        // Now we can insert some books for Agatha and Lew. We skip
the image attribute for now (handling blobs is a different example).
        ::LibDb::SQL::insert_into(books, (books.title = "Sie kamen
nach Bagdad", books.price = 12.9, books.author_id =
agatha_id)).execute(session);
        ::LibDb::SQL::insert_into(books, (books.title = "Ein
gefaehrlicher Gegner", books.price = 15.9, books.author_id =
agatha_id)).execute(session);
        ::LibDb::SQL::insert_into(books, (books.title = "Krieg und
Frieden", books.price = 21.9, books.author_id =
lew_id)).execute(session);

        // Lets query the number of books by Agatha.
        unsigned books_by_agatha;
        ::LibDb::SQL::select(::LibDb::SQL::Aggregation::count(books.author_id)
>> books_by_agatha).where(books.author_id ==
agatha_id).execute(session);
        BOOST_CHECK_EQUAL(books_by_agatha, 2);

        // Now lets query the sum of book prices per author. We'd like
to know the sum of book prices along with the author's last name.
        std::string author_name;
        double sum_book_prices;

        // ::LibDb::SQL::select binds the first record when it is
executed. To iterate over all records we need to use its return type:
a cursor.
        // The cursor provides a custom iteration interface with.
Below we'll see an example to convert the entire result-set of the
cursor
        // to an STL sequence.
        ::LibDb::SQL::Cursor cursor = ::LibDb::SQL::select(
          (authors.last_name >> author_name,
::LibDb::SQL::Aggregation::sum(books.price) >> sum_book_prices) //
selection from multiple tables
        ).where(
          authors.id == books.author_id // join by author ids
        ).group_by(
          books.author_id // group per author, so summing is performed
over books per author.
        ).order_by(
          authors.last_name, ::LibDb::SQL::OrderModes::DESC // sort
descending by authors last name
        ).execute(session);

On Sun, Sep 6, 2009 at 11:14 AM, joel<joel.falcou_at_[hidden]> wrote:
> Jean-Louis Leroy wrote:
>>
>> 2. It looks like the options they are considering for a syntax are mainly
>> inspired by Soci and pqxx. They have this in common : the query is expressed
>> as SQL text (à la Boost.Regex) that is parsed at run time instead of using
>> language constructs (à la Boost.Xpressive) that can be checked at compile
>> time. This is error prone : you can pass illegal SQL ; and the returned
>> values will incur runtime typecasting, which may fail at run time in case of
>> mismatch.
>
> Back at Boost'Con 09 I proposed a function based and not text based SQL
> interface using proto that looked like :
>
> #include <boost/rdb_proto/rdb.hpp>
>
> BOOST_RDB_REGISTER_FIELD(age,int)
> BOOST_RDB_REGISTER_FIELD(id ,int)
> BOOST_RDB_REGISTER_NAMED_FIELD(name,std::string,"complete_name")
>
> int main()
> {
>  connection db("some.db.url","login","passwd");
>
>  BOOST_RDB_TABLE((id)(name)(age))  employee("employee",db);
>  BOOST_RDB_RESULT_SET((age)(name)) rows;
>
>  rows = select(age,name).from(employee)
>                                          .where(   age > 45  && name ~=
> "Dil%" );
>
>  // Some random access
>  if( !r.empty() )
>   cout << r[0][name] << " is "
>        << r[0][age]  << " years old." << endl;
> }
>
> I have the whole ppt of my proposal somewhere if soemone's interested.
> I have alas not much free time for this but I had some prototype code laying
> somewhere
>
> --
> ___________________________________________
> Joel Falcou - Assistant Professor
> PARALL Team - LRI - Universite Paris Sud XI
> Tel : (+33)1 69 15 66 35
>
>
> _______________________________________________
> Unsubscribe & other changes:
> http://lists.boost.org/mailman/listinfo.cgi/boost
>


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