SOCI / soci

Official repository of the SOCI - The C++ Database Access Library
http://soci.sourceforge.net/
Boost Software License 1.0
1.41k stars 477 forks source link

User-defined types for non-wrapper types #979

Open Krzmbrzl opened 2 years ago

Krzmbrzl commented 2 years ago

My ultimate goal is to have a query of the form SELECT a, b FROM ... and then use a soci::use(container) where container is a std::vector< std::pair< std::string, std::string > >. Thus, my thinking was that if I can teach SOCI about the standard pair type, this should work.

I am aware of http://soci.sourceforge.net/doc/release/4.0/types/#user-defined-c-types and http://soci.sourceforge.net/doc/release/4.0/types/#object-relational-mapping.

However, I don't like the idea on depending on my columns to be required to have specific names for this to work, so the object-relational mapping seems to be ruled out.

The example in the documentation about user-defined types unfortunately only demonstrates how to write the conversion functions for a simple wrapper type that wraps an already supported type. From this I can't seem to generalize the syntax enough to get an idea how to go about writing the conversion functions for the std::pair type (given that this is a two-to-one mapping). Could someone point me into the right direction for how something like this is supposed to be done?

vadz commented 2 years ago

There is a 1-to-1 mapping between query variables and "exchange" parameters currently, so I don't think you can pass a pair to select a, b ... (and I don't understand at all what is the vector doing here).

Krzmbrzl commented 2 years ago

The vector is there to perform a bulk operation as there will be multiple key-value pairs to fetch.

There is a 1-to-1 mapping between query variables and "exchange" parameters currently, so I don't think you can pass a pair to select a, b

Hm okay. Then I'll have to see if I can abuse the dynamic binding via the row type for my purposes. Thanks!

vadz commented 2 years ago

I was confused by the use of vector because you used use, not into, but yes, it would make sense to use it with into. Unfortunately this still doesn't help with the pair...

zann1x commented 2 years ago

I haven't checked if this works, but SOCI once had a boost::tuple integration that you could take as a reference and adapt for your needs:

https://github.com/SOCI/soci/blob/98e0b8b7866ff6f9f0184ddea08d0c3b723cb77b/src/core/boost-tuple.h#L41-L61

Krzmbrzl commented 2 years ago

because you used use, not into, but yes, it would make sense to use it with into

Ah whoops - yeah I messed up my example :facepalm:

SOCI once had a boost::tuple integration that you could take as a reference and adapt for your need

I will definitely try this out. Thanks!

Krzmbrzl commented 2 years ago

Okay from quickly plying with boost::tuple it seems that it works when used as a single type (or at least it compiles) whereas it throws errors about incomplex exchage-types when used as std::vector< boost::tuple > :shrug:

zann1x commented 2 years ago

I just remembered why std::vector<boost::tuple> can't work:

https://github.com/SOCI/soci/blob/4ae2d90c5f7e4b864e149a63364d68a15c7806b2/include/soci/values-exchange.h#L138-L144

I have no idea why SOCI has the limitation of not supporting bulk ORM and I can't tell you right now what would be needed to add support for it.

But personally, whenever I need bulk ORM, I simply use soci::rowset and move the results to the desired vector in a second step. E.g.:

namespace soci
    template<>
    struct type_conversion<std::pair<std::string, std::string>>
    {
        typedef values base_type;

        static void from_base(values const& in, indicator, std::pair<std::string, std::string>& out)
        {
            in >> std::get<0>(out)
               >> std::get<1>(out);
        }

        static void to_base(std::pair<std::string, std::string> const& in, values& out, indicator& ind)
        {
            out << std::get<0>(in)
                << std::get<1>(in);
            ind = i_ok;
        }
    };
}

std::vector<std::pair<std::string, std::string>> get_foobar(soci::session& sql)
{
    soci::rowset<std::pair<std::string, std::string>> rs = (sql.prepare << "SELECT a, b FROM foobar");

    return { std::make_move_iterator(rs.begin()), std:make_move_iterator(rs.end()) };
}