soci icon indicating copy to clipboard operation
soci copied to clipboard

Bulk operations with user-defined types (4.0.0)

Open irodushka opened this issue 6 years ago • 13 comments

Hi

I'm looking at bulk operations and see that 4.0.0 documentation says:

Bulk operations are supported also for std::vectors of the user-provided types that have appropriate conversion routines defines.

Does it mean that it's possible to insert to a vector of structs now, like std::vector<Person>? Or this option is still unavailable in SOCI?

The issue is that I get a compilation error(s) while trying to do such a bulk insert -

In file included from /usr/local/include/soci/column-info.h:12,
                 from /usr/local/include/soci/soci.h:16,
                 from test6.cpp:1:
/usr/local/include/soci/type-conversion.h: In instantiation of ‘soci::details::use_type_ptr soci::details::do_use(T&, const string&, soci::details::user_type_tag) [with T = std::vector<Person>; soci::details::use_type_ptr = soci::details::type_ptr<soci::details::use_type_base>; std::string = std::basic_string<char>]’:
/usr/local/include/soci/bind-values.h:116:22:   required from ‘void soci::details::use_type_vector::exchange_(const soci::details::use_container<T, void>&, ...) [with T = std::vector<Person>]’
/usr/local/include/soci/bind-values.h:47:9:   required from ‘void soci::details::use_type_vector::exchange(const soci::details::use_container<T, Indicator>&) [with T = std::vector<Person>; Indicator = void]’
/usr/local/include/soci/ref-counted-prepare-info.h:42:7:   required from ‘void soci::details::ref_counted_prepare_info::exchange(const soci::details::use_container<T, Indicator>&) [with T = std::vector<Person>; Indicator = void]’
/usr/local/include/soci/prepare-temp-type.h:50:9:   required from ‘soci::details::prepare_temp_type& soci::details::prepare_temp_type::operator,(const soci::details::use_container<T, Indicator>&) [with T = std::vector<Person>; Indicator = void]’
test6.cpp:68:153:   required from here
/usr/local/include/soci/type-conversion.h:444:12: error: no matching function for call to ‘soci::details::type_ptr<soci::details::use_type_base>::type_ptr(soci::details::conversion_use_type<std::vector<Person> >*)’
     return use_type_ptr(new conversion_use_type<T>(t, name));
            ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

See my example code & full g++ output in attach:

err.log test6.zip

Regards Yuri.

irodushka avatar Oct 26 '19 09:10 irodushka

AFAIR this should be supported, but user-defined types need conversion routines. You will find examples in https://github.com/SOCI/soci/blob/master/tests/common-tests.h

BTW, looks like that specific case for vector<Person> is not covered in the tests. Contributions welcome.

mloskot avatar Oct 26 '19 11:10 mloskot

If you loоk into my sample code you see that I have a conversion implemented. Moreover - if I write use(pv[0]) instead of use(pv) it compiles ok. Maybe I need to create another conversion for vector itself?.. Or I should implement set/get methods for Person class?.. You know, it's hard to guess and a test sample would be very useful here:)

irodushka avatar Oct 26 '19 12:10 irodushka

Sorry, I currently have no time to dig the problem myself. That's why I suggested playing with simple test, trying bulk ops with the PhonebookEntry classes in tests. Simply, try DIY

mloskot avatar Oct 26 '19 12:10 mloskot

Tried to write additional tests. It does not compile any way. If use(bulk_vector) changed to use(bulk_vector[0]) - compiles ok. What am I doing wrong?:)

(Modded common_tests.h in attach)

TEST_CASE_METHOD(common_tests, "Bulk insert with ORM", "[core][orm]")
{
    soci::session sql(backEndFactory_, connectString_);

    sql.uppercase_column_names(true);
    auto_table_creator tableCreator(tc_.table_creator_3(sql));
    
    std::vector<PhonebookEntry> bulk_vector;
    for( int i = 0; i < 100; i++ ) {
        PhonebookEntry temp;
        std::ostringstream oss;
        oss << "bulk" << i;
        temp.name = temp.phone = oss.str();

        bulk_vector.push_back( temp );
    }

    statement insertStatement = (sql.prepare << "insert into soci_test values (:NAME, :PHONE)", use(bulk_vector));
    insertStatement.execute(true);

    int count = 0;

    sql << "select count(*) from soci_test where NAME like ('bulk%')", into(count);

    CHECK(count == 100);
}
SECTION("Bulk insert from vector of boost::fusion::vector")
{
    typedef boost::fusion::vector<double, int, std::string> bfv_t;
    typedef std::vector<bfv_t> v_t;

    v_t bulk_vector;
    for( int i = 0; i < 100; i++ ) {
        bulk_vector.push_back( bfv_t( double(i)/2, i, "Joe Hacker" ) );
    }

    sql << "insert into soci_test(num_float, num_int, name) values(:d, :i, :s)", use(bulk_vector);

    sql << "delete from soci_test";
}

common-tests.zip

irodushka avatar Oct 27 '19 14:10 irodushka

I encountered same problem. It does have a bug, but the root cause is this library doesn't support in my opinion.

The bug I found is at type-conversion.h. Missing std:: at the third parameter. Compiler can't figure out ::size_t and instantiate correctly.

After fixed that, this header(values-exchange.h) refused the bulk operation with ORM at compile time. I have no idea why and simply comment it. Finally it throws exception in backend.

In my case, MySQL backend doesn't implement define_by_pos_bulk which postgresql did. The only way is implementing this function and the compile time protection.

Even though you resolves these problems, you still have to resize this vector to specify max size which is not convenient to me, too big is a waste or you have to fetch multiple times if it is too small. I think fetch and push_back is a better way. e.g. https://github.com/SOCI/soci/blob/master/tests/common-tests.h#L767

xnum avatar Nov 26 '19 04:11 xnum

@xnum I use the postgres backend and also can't perform bulk operations. I have implemented conversion routines for my classes but the problem is still there. Did you check that on postgres?

jimmyhollywood avatar Feb 04 '21 15:02 jimmyhollywood

Could you please retry with the latest master? There were several changes possibly affecting this, so it might work now.

If it still doesn't, a patch/PR adding a failing unit test would be very welcome. TIA!

vadz avatar Apr 07 '21 03:04 vadz

@vadz This doesn't seem to work in the latest master either.

MRE :

class TestClass
{

public:

    int value;

    TestClass() : value(0) {}
    TestClass(int value) : value(value) {}
};

namespace soci
{
    template<>
    struct type_conversion<TestClass>
    {
        typedef values base_type;

        static void from_base(const base_type & bt, indicator /*ind*/, TestClass & tc)
	{
            tc.value = bt.get<int>("value");
        }

        static void to_base(const TestClass & tc, base_type & bt, indicator & ind)
	{
            bt.set("value", tc.value);
            ind = i_ok;
        }
    };
}

int main()
{
    // [...]

    std::vector<TestClass> tests;

    for (int i = 0; i < 10; ++i)
    {
        tests.emplace_back(i);
    }

    // This line does not compile
    soci::statement statement = (session.prepare << "INSERT INTO TestClass (value) VALUES (:value)", soci::use(tests));

    return 0;
}

e1y4s avatar May 31 '23 18:05 e1y4s

@vadz @Ely4s

This issue is still causing problems, I've created the failing unit test here: https://github.com/SOCI/soci/pull/1065

AndyStieber avatar Jul 27 '23 16:07 AndyStieber

Same problem here... Did someone find a way to insert the std::vector<Person> in the database?

albertcp avatar Feb 22 '24 15:02 albertcp

You can insert a vector of simple types just fine, of course, and also a vector of a type convertible to some simple type. Inserting a vector using ORM-like API, with the object mapping to multiple columns doesn't work AFAICS and I'm not sure how to make it work.

vadz avatar Feb 22 '24 17:02 vadz

Well.. in my case Im trying to insert and select an array of custom object from the database. For the select operation, i'm using an rowset, without any problem. But for the insert operation.. is not the same. I have a type_conversion operation for the class Person and a type_conversion operation for the std::vector<Person>. When I insert 1 Person it works, but when I try to insert an std::vector<Person> it does compile. It is not possible to link a std::vector<CustomObject> to a type_conversion operation?

albertcp avatar Feb 23 '24 12:02 albertcp

Right now I don't think it's possible, but it probably could be implemented.

vadz avatar Feb 23 '24 13:02 vadz