diff --git a/include/soci/values.h b/include/soci/values.h index 50c78d260..426902f42 100644 --- a/include/soci/values.h +++ b/include/soci/values.h @@ -165,8 +165,17 @@ class SOCI_DECL values indicator * pind = new indicator(indic); indicators_.push_back(pind); + // If a NULL indicator was passes by the user, we must preserve it, + // even if conversion to the base type was successful. + indicator conv_ind = i_ok; + base_type baseValue{}; - type_conversion::to_base(value, baseValue, *pind); + type_conversion::to_base(value, baseValue, conv_ind); + + if (indic != i_null) + { + *pind = conv_ind; + } details::copy_holder * pcopy = new details::copy_holder(baseValue); @@ -196,8 +205,18 @@ class SOCI_DECL values indicators_.push_back(pind); typedef typename type_conversion::base_type base_type; + + // If a NULL indicator was passes by the user, we must preserve it, + // even if conversion to the base type was successful. + indicator conv_ind = i_ok; + base_type baseValue; - type_conversion::to_base(value, baseValue, *pind); + type_conversion::to_base(value, baseValue, conv_ind); + + if (indic != i_null) + { + *pind = conv_ind; + } details::copy_holder * pcopy = new details::copy_holder(baseValue); diff --git a/tests/common/test-common.cpp b/tests/common/test-common.cpp index ffe9e1c88..96c42fafb 100644 --- a/tests/common/test-common.cpp +++ b/tests/common/test-common.cpp @@ -1501,6 +1501,96 @@ TEST_CASE_METHOD(common_tests, "Use type conversion", "[core][use]") } } +// "values" tests +TEST_CASE_METHOD(common_tests, "Use values", "[core][use][values]") +{ + soci::session sql(backEndFactory_, connectString_); + + auto_table_creator tableCreator(tc_.table_creator_1(sql)); + + const int u_id = 1; + const char u_c('a'); + const std::string u_s = "Hello SOCI!"; + const double u_d = 2.0; + + SECTION("Named with all i_ok indicators") + { + soci::values use_values; + + use_values.set ("id", u_id, i_ok); + use_values.set ("c", u_c, i_ok); + use_values.set ("str", u_s, i_ok); + use_values.set ("d", u_d, i_ok); + + CHECK (i_ok == use_values.get_indicator ("id")); + CHECK (i_ok == use_values.get_indicator ("c")); + CHECK (i_ok == use_values.get_indicator ("str")); + CHECK (i_ok == use_values.get_indicator ("d")); + + sql << "insert into soci_test(id, c, str, d) values(:id, :c, :str, :d)", + use(use_values); + + int i_id{}; + char i_c{}; + std::string i_s{}; + double i_d{}; + + sql << "select id, c, str, d from soci_test", + into(i_id), into(i_c), into(i_s), into(i_d); + + CHECK(i_id == 1); + CHECK(i_c == 'a'); + CHECK(i_s == "Hello SOCI!"); + CHECK(i_d == 2.0); + } + + SECTION("Named with all i_null indicators") + { + soci::values use_values; + + use_values.set ("id", u_id, i_null); + use_values.set ("c", u_c, i_null); + use_values.set ("str", u_s, i_null); + use_values.set ("d", u_d, i_null); + + CHECK (i_null == use_values.get_indicator ("id")); + CHECK (i_null == use_values.get_indicator ("c")); + CHECK (i_null == use_values.get_indicator ("str")); + CHECK (i_null == use_values.get_indicator ("d")); + + sql << "insert into soci_test(id, c, str, d) values(:id, :c, :str, :d)", + use(use_values); + + int i_id{}; + char i_c{}; + std::string i_s{}; + double i_d{}; + + indicator id_ind; + indicator c_ind; + indicator s_ind; + indicator d_ind; + + sql << "select id, c, str, d from soci_test", + into(i_id, id_ind), into(i_c, c_ind), into(i_s, s_ind), + into(i_d, d_ind); + + CHECK (int{} == i_id); + /* + Skip checking i_c, because char processing differs between backends: + Oracle backend returns ' ' instead of char{} or not touching the + value at all. + */ + CHECK (std::string{} == i_s); + CHECK (double{} == i_d); + + CHECK(id_ind == i_null); + CHECK(c_ind == i_null); + CHECK(s_ind == i_null); + CHECK(d_ind == i_null); + } +} + // test for multiple use (and into) elements TEST_CASE_METHOD(common_tests, "Multiple use and into", "[core][use][into]") {