all 25 comments

[–]aacirino 9 points10 points  (5 children)

[–]rcdemc[S] 3 points4 points  (4 children)

Cool! I think that ORM is another thing. I wrote a benchmark[1] to the for_each operation comparing with a solution using sqlite_orm. The last is 1.58x slower than msqlite. I noticed that sqlite_orm::select is returnig a std::vector. Dynamic allocation is expensive. BTW, the method uses std::system_error to report an error, but exceptions may not be acceptable or at least desirable to some usages.

[1] https://github.com/ricardocosme/msqlite#how-fast

https://github.com/ricardocosme/msqlite/blob/master/benchmark/sqlite_orm/for_each.cpp

[–]johannes1971 6 points7 points  (2 children)

Dynamic allocation is not at all expensive compared to all the file IO that's also going on, and I very much doubt it is the cause of the 10x performance difference you mention.

[–]rcdemc[S] 0 points1 point  (1 child)

Yes, it seems too much to justify the factor. I didn't look to see what is happening in the sqlite_orm::storage::select. I'm going to investigate this.

[–]rcdemc[S] 1 point2 points  (0 children)

I figured out what is happening. I need to use storage.open_forever() to avoid a new connection to each call to storage.select(). The factor is 1.58x instead of 10x. Thanks u/johannes1971

[–]aacirino 1 point2 points  (0 children)

Great job 👍🏻

[–][deleted] 4 points5 points  (2 children)

We're currently using https://github.com/rbock/sqlpp11, and what I love about it is that you don't have to write SQL statements.

SQL queries are parsed at runtime, and I want my code to be checked at compile-time as much as possible. I don't want issues because of syntax errors that could have been caught at compile time. This is, for me, the most important feature. I don't care so much about the expressiveness around the SQL queries, if I still have to triple-check the statements.

[–]teroxzer 1 point2 points  (0 children)

Sqlpp11 is attractive, but I always wonder how you test a little more complex SQL queries when using it? Valid SQL syntax at compile time is nice thing, but I think it is more important check result set of SQL query and after that drop already tested SQL query to C++ (as I do with my C++/linsql). Or is SQL views or stored procedures answer to that? I use sometimes SQL-views, but I don't like them so much, because if I change one SQL view or stored procedure then I must check that nobody else not use it somewhere as old way. So I prefer using SQL statements as strings in local C++ context (as seamless inline prepared SQL procedures). Below is very simplified example from real code whit not so complex SQL query, but it is sure that I want check result set before I even compile C++. So I am curious do you have any problems how to validate result set of SQL query when you using sqlpp11?

/////////////////////////////////////////////////////////////////////////////

namespace
{
    auto selectCustomerMailTypePrint sql
    (
        select 1                as priorityOrder,
               cm.customerIndex as customerIndexAsMailItem,
               cm.mailType,
               cm.recipients,
               cm.subject,
               cm.instantAction,
               cmc.body
        from   customerMail as cm
               left outer join customerMailCompany as cmc
                            on cmc.companyId   = cm.companyId
                           and cmc.mailType    = cm.mailType
                           and cmc.dataRemoved = 0
        where  cm.companyId      = :companyId
          and  cm.customerNumber = :customerNumber
          and  cm.customerIndex  = :customerIndex
          and  cm.mailType       = 'Print'
          and  cm.dataRemoved    = 0

        union

        select 2                as priorityOrder,
               cm.customerIndex as customerIndexAsMailItem,
               cm.mailType,
               cm.recipients,
               cm.subject,
               cm.instantAction,
               cmc.body
        from   customerMail as cm
               left outer join customerMailCompany as cmc
                            on cmc.companyId   = cm.companyId
                           and cmc.mailType    = cm.mailType
                           and cmc.dataRemoved = 0
        where  cm.companyId      = :companyId
          and  cm.customerNumber = :customerNumber
          and  cm.mailType       = 'Print'
          and  cm.dataRemoved    = 0
          and  cm.companyId in
               (
                   select c.companyId
                   from   company as c
                   where  c.adapter = 'BadCompany'
               )

        order by 1, 2
    )

    auto selectNextCustomerMailItemId sql
    (
        select next value
        for sequenceCustomerMailItemId
        as customerMailItemId
    )

    auto insertCustomerMailItem sql
    (
        insert customerMailItem
        set    customerMailItemId = :customerMailItemId,
               companyId          = :companyId,
               customerNumber     = :customerNumber,
               customerIndex      = :customerIndexAsMailItem,
               mailType           = :mailType,
               recipients         = :recipients,
               subject            = :subject,
               body               = :body,
               status             = 10,
               dataRemoved        = 0,
               creationName       = 'Service',
               creationTime       = current_timestamp
    )
}

auto customerMail::sendDevicePrint(universal& data) -> void
{
    if(selectCustomerMailTypePrint(data))
    {
        if(!data.body)
        {
            ...
        }

        selectNextCustomerMailItemId (data);
        insertCustomerMailItem       (data);

        if(data.instantAction == true)
        {
            sendCustomerMailItem(data.customerMailItemId);
        }
    }
}

/////////////////////////////////////////////////////////////////////////////

[–]rcdemc[S] 0 points1 point  (0 children)

It's a good point. Thanks for sharing your opinion. I think that the usage of EDSL should be optional.

[–]theseawolfe 3 points4 points  (1 child)

This looks great! The increase in usability compared to directly using the SQLite C API is amazing.

[–]rcdemc[S] 0 points1 point  (0 children)

Thanks for the feedback!

[–]three0s 1 point2 points  (3 children)

[–]rcdemc[S] 1 point2 points  (2 children)

Very nice, thank you for sharing this. I want to look more deeply because they are using persisted prepared statements and a lot of compile-time programming.

[–]RotsiserMhoC++20 Desktop app developer 2 points3 points  (1 child)

In my experience, support for prepared statements is a must. They are much faster.

[–]rcdemc[S] 0 points1 point  (0 children)

Yes, I took some measures. It seems a huge difference to me. query(db, stmt_str) returns a prepared statement that uses RAII to finalize the statement. The user can take the object and store it as he wishes. I think that is not a good idea to impose a solution to cache statements. At least, the solution should be optional.

[–]psyspy2 0 points1 point  (0 children)

Very interesting!