From a92838cd0b8f98d4876574a356e1277284d37d9c Mon Sep 17 00:00:00 2001 From: Alexey Palienko Date: Thu, 23 Nov 2023 20:53:40 +0300 Subject: [PATCH 1/4] Allow passing rvalues to soci::use() --- include/soci/bind-values.h | 20 ++++- include/soci/statement.h | 8 +- include/soci/type-conversion.h | 27 +++++++ include/soci/use-type.h | 42 ++++++++++ include/soci/use.h | 20 +++-- tests/oracle/test-oracle.cpp | 144 +++++++++++++++++++++++++++------ 6 files changed, 221 insertions(+), 40 deletions(-) diff --git a/include/soci/bind-values.h b/include/soci/bind-values.h index e84fb0e63..636dbc085 100644 --- a/include/soci/bind-values.h +++ b/include/soci/bind-values.h @@ -40,8 +40,8 @@ class use_type_vector: public std::vector void exchange(use_type_ptr const& u) { push_back(u.get()); u.release(); } - template - void exchange(use_container const &uc) + template + void exchange ( use_container const &uc ) { #ifdef SOCI_HAVE_BOOST exchange_(uc, (typename boost::fusion::traits::is_sequence::type *)NULL); @@ -111,10 +111,24 @@ class use_type_vector: public std::vector void exchange_(use_container const &uc, ...) { exchange(do_use(uc.t, uc.ind, uc.name, typename details::exchange_traits::type_family())); } + template + void exchange_ ( use_container const &uc, ... ) + { + using type_family_tag = typename details::exchange_traits::type_family; + exchange ( do_use ( uc.t, uc.ind, uc.name, type_family_tag (), std::true_type () ) ); + } + template - void exchange_(use_container const &uc, ...) + void exchange_ ( use_container const &uc, ... ) { exchange(do_use(uc.t, uc.name, typename details::exchange_traits::type_family())); } + template + void exchange_ ( use_container const &uc, ... ) + { + using type_family_tag = typename details::exchange_traits::type_family; + exchange ( do_use ( uc.t, uc.name, type_family_tag (), std::true_type () ) ); + } + template void exchange_(use_container const &uc, ...) { exchange(do_use(uc.t, uc.ind, uc.name, typename details::exchange_traits::type_family())); } diff --git a/include/soci/statement.h b/include/soci/statement.h index b356762e0..a5064f025 100644 --- a/include/soci/statement.h +++ b/include/soci/statement.h @@ -50,8 +50,8 @@ class SOCI_DECL statement_impl { intos_.exchange(ic); } void exchange(use_type_ptr const & u) { uses_.exchange(u); } - template - void exchange(use_container const &uc) + template + void exchange ( use_container const &uc ) { uses_.exchange(uc); } @@ -198,8 +198,8 @@ class SOCI_DECL statement template void exchange(details::into_container const &ic) { impl_->exchange(ic); } void exchange(details::use_type_ptr const & u) { impl_->exchange(u); } - template - void exchange(details::use_container const &uc) { impl_->exchange(uc); } + template + void exchange(details::use_container const &uc) { impl_->exchange(uc); } void clean_up() { impl_->clean_up(); } void bind_clean_up() { impl_->bind_clean_up(); } diff --git a/include/soci/type-conversion.h b/include/soci/type-conversion.h index 3b96ab770..978f16a0c 100644 --- a/include/soci/type-conversion.h +++ b/include/soci/type-conversion.h @@ -183,6 +183,21 @@ class conversion_use_type SOCI_NOT_COPYABLE(conversion_use_type) }; +template +class by_value_conversion_use_type : value_holder, public conversion_use_type +{ +public: + by_value_conversion_use_type ( T const& t, std::string const& name = std::string () ) + : value_holder ( t, i_ok ), conversion_use_type ( value_holder::saved_val_, name ) + { + } + + by_value_conversion_use_type ( T const& t, const indicator& ind, std::string const& name = std::string () ) + : value_holder ( t, ind ), conversion_use_type ( value_holder::saved_val_, value_holder::saved_ind_, name ) + { + } +}; + // this class is used to ensure correct order of construction // of vector based into_type and use_type elements that use type_conversion @@ -440,6 +455,18 @@ into_type_ptr do_into(std::vector & t, std::vector & ind, new conversion_into_type >(t, ind, begin, end)); } +template +use_type_ptr do_use ( T& t, std::string const& name, user_type_tag, std::true_type ) +{ + return use_type_ptr ( new by_value_conversion_use_type ( t, name ) ); +} + +template +use_type_ptr do_use ( T& t, indicator& ind, std::string const& name, user_type_tag, std::true_type ) +{ + return use_type_ptr ( new by_value_conversion_use_type ( t, ind, name ) ); +} + template use_type_ptr do_use(T & t, std::string const & name, user_type_tag) { diff --git a/include/soci/use-type.h b/include/soci/use-type.h index 6016335c9..b9f33e06d 100644 --- a/include/soci/use-type.h +++ b/include/soci/use-type.h @@ -204,6 +204,36 @@ class use_type : public standard_use_type {} }; +template +struct value_holder +{ + using value_type = T; + value_holder ( const T& t, const indicator& ind ) + : saved_val_ ( t ), + saved_ind_ ( ind ) + { + } + T saved_val_; + indicator saved_ind_; +}; + + +template +class by_value_use_type : value_holder, public use_type +{ +public: + by_value_use_type ( T const& t, std::string const& name = std::string () ) + : value_holder (t, i_ok), + use_type ( value_holder::saved_val_, name ) + {} + + by_value_use_type ( T const& t, const indicator& ind, std::string const& name = std::string () ) + : value_holder ( t, ind ), + use_type ( value_holder::saved_val_, value_holder::saved_ind_, name ) + {} +}; + + template class use_type > : public vector_use_type { @@ -257,6 +287,18 @@ class use_type > : public vector_use_type // helper dispatchers for basic types +template +use_type_ptr do_use ( T& t, std::string const& name, basic_type_tag, std::true_type ) +{ + return use_type_ptr ( new by_value_use_type ( t, name ) ); +} + +template +use_type_ptr do_use ( T& t, indicator& ind, std::string const& name, basic_type_tag, std::true_type ) +{ + return use_type_ptr ( new by_value_use_type ( t, ind, name ) ); +} + template use_type_ptr do_use(T & t, std::string const & name, basic_type_tag) { diff --git a/include/soci/use.h b/include/soci/use.h index 9d0531020..c33ec1cdb 100644 --- a/include/soci/use.h +++ b/include/soci/use.h @@ -18,7 +18,7 @@ namespace soci namespace details { -template +template struct use_container { use_container(T &_t, Indicator &_ind, const std::string &_name) @@ -32,8 +32,8 @@ struct use_container }; typedef void no_indicator; -template -struct use_container +template +struct use_container { use_container(T &_t, const std::string &_name) : t(_t), name(_name) {} @@ -48,10 +48,16 @@ struct use_container // soci::use is deleted for rvalues because it will likely lead to subtle stack-use-after-scope bugs. template -details::use_container use(T &&t, const std::string &name = std::string()) = delete; +auto use ( T &&t, const std::string &name = std::string () ) +{ + return details::use_container ( t, name ); +} template -details::use_container use(T &&t, indicator & ind, std::string const &name = std::string()) = delete; +auto use ( T &&t, indicator &&ind, std::string const &name = std::string () ) +{ + return details::use_container ( t, ind, name ); +} template details::use_container use(T &t, const std::string &name = std::string()) @@ -66,8 +72,8 @@ details::use_container use(T &t, indicator & ind, std::string cons { return details::use_container(t, ind, name); } template -details::use_container use(T const &t, indicator & ind, std::string const &name = std::string()) -{ return details::use_container(t, ind, name); } +auto use(T const &t, indicator const & ind, std::string const &name = std::string()) +{ return details::use_container(t, ind, name); } // vector containers template diff --git a/tests/oracle/test-oracle.cpp b/tests/oracle/test-oracle.cpp index 13e24443c..8e07cbe53 100644 --- a/tests/oracle/test-oracle.cpp +++ b/tests/oracle/test-oracle.cpp @@ -180,39 +180,68 @@ struct basic_table_creator : public table_creator_base } }; -TEST_CASE("Oracle nested statement", "[oracle][blob]") +TEST_CASE("Oracle nested statement", "[oracle][cursors]") { soci::session sql(backEnd, connectString); basic_table_creator tableCreator(sql); - int id; std::string name; - { - statement st1 = (sql.prepare << - "insert into soci_test (id, name) values (:id, :name)", - use(id), use(name)); + statement st1 = (sql.prepare << + "insert into soci_test (id, name) values (:id, :name)", + use(id), use(name)); + + id = 1; name = "John"; st1.execute(1); + id = 2; name = "Anna"; st1.execute(1); + id = 3; name = "Mike"; st1.execute(1); + + { + statement stInner(sql); + statement stOuter = (sql.prepare << + "select cursor(select name from soci_test order by id)" + " from soci_test where id = 1", + into(stInner)); + stInner.exchange(into(name)); + stOuter.execute(); + stOuter.fetch(); + + std::vector names; + while (stInner.fetch()) { names.push_back(name); } + + REQUIRE(names.size() == 3); + CHECK(names[0] == "John"); + CHECK(names[1] == "Anna"); + CHECK(names[2] == "Mike"); + } + { + statement stInner ( sql ); + statement stOuter ( sql ); + stOuter.alloc (); + stOuter.prepare + ( + "declare\n" + " cur sys_refcursor;\n" + "begin\n" + " open cur for select name from soci_test order by id;\n" + " :vCurs := cur;\n" + "end;\n" + ); + stOuter.exchange ( use ( stInner ) ); + stOuter.define_and_bind (); + stOuter.execute (true); + //stOuter.fetch (); + stInner.exchange ( into ( name ) ); + stInner.define_and_bind (); + std::vector names; + while ( stInner.fetch () ) + { + names.push_back ( name ); + } - id = 1; name = "John"; st1.execute(1); - id = 2; name = "Anna"; st1.execute(1); - id = 3; name = "Mike"; st1.execute(1); + REQUIRE ( names.size () == 3 ); + CHECK ( names[0] == "John" ); + CHECK ( names[1] == "Anna" ); + CHECK ( names[2] == "Mike" ); } - - statement stInner(sql); - statement stOuter = (sql.prepare << - "select cursor(select name from soci_test order by id)" - " from soci_test where id = 1", - into(stInner)); - stInner.exchange(into(name)); - stOuter.execute(); - stOuter.fetch(); - - std::vector names; - while (stInner.fetch()) { names.push_back(name); } - - REQUIRE(names.size() == 3); - CHECK(names[0] == "John"); - CHECK(names[1] == "Anna"); - CHECK(names[2] == "Mike"); } @@ -386,6 +415,69 @@ TEST_CASE("Oracle null user-defined objects in/out", "[oracle][null][type_conver CHECK(ind == i_null); } +TEST_CASE ( "Oracle", "[oracle][rvalue]" ) +{ + soci::session sql ( backEnd, connectString ); + // base types + { + std::string r; + statement s ( sql ); + s.alloc (); + s.prepare ( "select :t from dual" ); + { + s.exchange ( use ( 3l ) ); + s.exchange ( into ( r ) ); + } + s.define_and_bind (); + s.execute ( true ); + CHECK ( r == "3" ); + } + { + std::string r; + indicator r_ind; + statement s ( sql ); + s.alloc (); + s.prepare ( "select :t from dual" ); + { + s.exchange ( use ( 3l, i_null ) ); + s.exchange ( into ( r, r_ind ) ); + } + s.define_and_bind (); + s.execute ( true ); + CHECK ( r.empty () ); + CHECK ( r_ind == i_null ); + } + // conversion types + { + std::string r; + statement s ( sql ); + s.alloc (); + s.prepare ( "select :t from dual" ); + { + s.exchange ( use ( string_holder("3") ) ); + s.exchange ( into ( r ) ); + } + s.define_and_bind (); + s.execute ( true ); + CHECK ( r == "3" ); + } + { + std::string r; + indicator r_ind; + statement s ( sql ); + s.alloc (); + s.prepare ( "select :t from dual" ); + { + s.exchange ( use ( string_holder ( "3" ), i_null ) ); + s.exchange ( into ( r, r_ind ) ); + } + s.define_and_bind (); + s.execute ( true ); + CHECK ( r.empty () ); + CHECK ( r_ind == i_null ); + } +} + // test bulk insert features TEST_CASE("Oracle bulk insert", "[oracle][insert][bulk]") { From d004f2a9a8392a31938cecbe220fca8bb363e3ab Mon Sep 17 00:00:00 2001 From: Alexey Palienko Date: Fri, 24 Nov 2023 12:43:01 +0300 Subject: [PATCH 2/4] add rvalue test into common tests --- tests/common-tests.h | 67 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/tests/common-tests.h b/tests/common-tests.h index 9bd0773de..7787039a2 100644 --- a/tests/common-tests.h +++ b/tests/common-tests.h @@ -689,6 +689,73 @@ TEST_CASE_METHOD(common_tests, "Basic functionality", "[core][basics]") CHECK(fetchEnd == false); } +TEST_CASE_METHOD ( common_tests, "Rvalues in use", "[core][basics][rvalue]" ) +{ + soci::session sql ( backEndFactory_, connectString_ ); + + auto_table_creator tableCreator ( tc_.table_creator_1 ( sql ) ); + sql << "insert into soci_test (id) values (" << 123 << ")"; // we need one row in the table + + // base types + { + int r; + statement s ( sql ); + s.alloc (); + s.prepare ( "select :t from soci_test" ); + { + s.exchange ( use ( 3 ) ); + s.exchange ( into ( r ) ); + } + s.define_and_bind (); + s.execute ( true ); + CHECK ( r == 3 ); + } + { + int r{0}; + indicator r_ind; + statement s ( sql ); + s.alloc (); + s.prepare ( "select :t from soci_test" ); + { + s.exchange ( use ( 3, i_null ) ); + s.exchange ( into ( r, r_ind ) ); + } + s.define_and_bind (); + s.execute ( true ); + CHECK ( r == 0 ); + CHECK ( r_ind == i_null ); + } + // conversion types + { + int r; + statement s ( sql ); + s.alloc (); + s.prepare ( "select :t from soci_test" ); + { + s.exchange ( use ( MyInt ( 3 ) ) ); + s.exchange ( into ( r ) ); + } + s.define_and_bind (); + s.execute ( true ); + CHECK ( r == 3 ); + } + { + int r{-1}; + indicator r_ind; + statement s ( sql ); + s.alloc (); + s.prepare ( "select :t from soci_test" ); + { + s.exchange ( use ( MyInt ( 3 ), i_null ) ); + s.exchange ( into ( r, r_ind ) ); + } + s.define_and_bind (); + s.execute ( true ); + CHECK ( r == -1 ); + CHECK ( r_ind == i_null ); + } +} + // "into" tests, type conversions, etc. TEST_CASE_METHOD(common_tests, "Use and into", "[core][into]") { From 41e3410444164b47d03fe0582d967d0216eb34a9 Mon Sep 17 00:00:00 2001 From: Alexey Palienko Date: Fri, 24 Nov 2023 13:19:54 +0300 Subject: [PATCH 3/4] postgresql requires parameter's type --- tests/common-tests.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/common-tests.h b/tests/common-tests.h index 7787039a2..0e96ef0bf 100644 --- a/tests/common-tests.h +++ b/tests/common-tests.h @@ -701,7 +701,7 @@ TEST_CASE_METHOD ( common_tests, "Rvalues in use", "[core][basics][rvalue]" ) int r; statement s ( sql ); s.alloc (); - s.prepare ( "select :t from soci_test" ); + s.prepare ( "select :t::int from soci_test" ); { s.exchange ( use ( 3 ) ); s.exchange ( into ( r ) ); @@ -715,7 +715,7 @@ TEST_CASE_METHOD ( common_tests, "Rvalues in use", "[core][basics][rvalue]" ) indicator r_ind; statement s ( sql ); s.alloc (); - s.prepare ( "select :t from soci_test" ); + s.prepare ( "select :t::int from soci_test" ); { s.exchange ( use ( 3, i_null ) ); s.exchange ( into ( r, r_ind ) ); @@ -730,7 +730,7 @@ TEST_CASE_METHOD ( common_tests, "Rvalues in use", "[core][basics][rvalue]" ) int r; statement s ( sql ); s.alloc (); - s.prepare ( "select :t from soci_test" ); + s.prepare ( "select :t::int from soci_test" ); { s.exchange ( use ( MyInt ( 3 ) ) ); s.exchange ( into ( r ) ); @@ -744,7 +744,7 @@ TEST_CASE_METHOD ( common_tests, "Rvalues in use", "[core][basics][rvalue]" ) indicator r_ind; statement s ( sql ); s.alloc (); - s.prepare ( "select :t from soci_test" ); + s.prepare ( "select :t::int from soci_test" ); { s.exchange ( use ( MyInt ( 3 ), i_null ) ); s.exchange ( into ( r, r_ind ) ); From ef2fee6c69001670bf20b60e5ee5eb156eafd06d Mon Sep 17 00:00:00 2001 From: Alexey Palienko Date: Fri, 24 Nov 2023 15:14:09 +0300 Subject: [PATCH 4/4] fix error in test --- tests/common-tests.h | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/tests/common-tests.h b/tests/common-tests.h index 0e96ef0bf..d92e6c374 100644 --- a/tests/common-tests.h +++ b/tests/common-tests.h @@ -692,65 +692,65 @@ TEST_CASE_METHOD(common_tests, "Basic functionality", "[core][basics]") TEST_CASE_METHOD ( common_tests, "Rvalues in use", "[core][basics][rvalue]" ) { soci::session sql ( backEndFactory_, connectString_ ); - - auto_table_creator tableCreator ( tc_.table_creator_1 ( sql ) ); - sql << "insert into soci_test (id) values (" << 123 << ")"; // we need one row in the table - // base types { + auto_table_creator tableCreator ( tc_.table_creator_1 ( sql ) ); int r; statement s ( sql ); s.alloc (); - s.prepare ( "select :t::int from soci_test" ); + s.prepare ( "insert into soci_test (id) values (:t)" ); { s.exchange ( use ( 3 ) ); - s.exchange ( into ( r ) ); } s.define_and_bind (); s.execute ( true ); + sql << "select id from soci_test", into ( r ); CHECK ( r == 3 ); } { + auto_table_creator tableCreator ( tc_.table_creator_1 ( sql ) ); int r{0}; indicator r_ind; statement s ( sql ); s.alloc (); - s.prepare ( "select :t::int from soci_test" ); + s.prepare ( "insert into soci_test (id) values (:t)" ); { s.exchange ( use ( 3, i_null ) ); - s.exchange ( into ( r, r_ind ) ); } s.define_and_bind (); s.execute ( true ); + sql << "select id from soci_test", into ( r, r_ind ); CHECK ( r == 0 ); CHECK ( r_ind == i_null ); } // conversion types { + auto_table_creator tableCreator ( tc_.table_creator_1 ( sql ) ); int r; statement s ( sql ); s.alloc (); - s.prepare ( "select :t::int from soci_test" ); + s.prepare ( "insert into soci_test (id) values (:t)" ); { s.exchange ( use ( MyInt ( 3 ) ) ); - s.exchange ( into ( r ) ); } s.define_and_bind (); s.execute ( true ); + sql << "select id from soci_test", into ( r ); CHECK ( r == 3 ); } { + auto_table_creator tableCreator ( tc_.table_creator_1 ( sql ) ); int r{-1}; indicator r_ind; statement s ( sql ); s.alloc (); - s.prepare ( "select :t::int from soci_test" ); + s.prepare ( "insert into soci_test (id) values (:t)" ); { s.exchange ( use ( MyInt ( 3 ), i_null ) ); - s.exchange ( into ( r, r_ind ) ); } s.define_and_bind (); s.execute ( true ); + sql << "select id from soci_test", into ( r, r_ind ); CHECK ( r == -1 ); CHECK ( r_ind == i_null ); }