File indexing completed on 2025-02-16 05:12:46
0001 // 0002 // Copyright (C) 2004-2008 Maciej Sobczak, Stephen Hutton 0003 // Distributed under the Boost Software License, Version 1.0. 0004 // (See accompanying file LICENSE_1_0.txt or copy at 0005 // http://www.boost.org/LICENSE_1_0.txt) 0006 // 0007 0008 #ifndef SOCI_COMMON_TESTS_H_INCLUDED 0009 #define SOCI_COMMON_TESTS_H_INCLUDED 0010 0011 #include "soci/soci.h" 0012 0013 #ifdef SOCI_HAVE_BOOST 0014 // explicitly pull conversions for Boost's optional, tuple and fusion: 0015 #include <boost/version.hpp> 0016 #include "soci/boost-optional.h" 0017 #include "soci/boost-tuple.h" 0018 #include "soci/boost-gregorian-date.h" 0019 #if defined(BOOST_VERSION) && BOOST_VERSION >= 103500 0020 #include "soci/boost-fusion.h" 0021 #endif // BOOST_VERSION 0022 #endif // SOCI_HAVE_BOOST 0023 #ifdef SOCI_HAVE_CXX17 0024 #include "soci/std-optional.h" 0025 #endif 0026 0027 #include "soci-compiler.h" 0028 0029 #include "soci/callbacks.h" 0030 0031 #define CATCH_CONFIG_RUNNER 0032 #include <catch.hpp> 0033 0034 #if defined(_MSC_VER) && (_MSC_VER < 1500) 0035 #undef SECTION 0036 #define SECTION(name) INTERNAL_CATCH_SECTION(name, "dummy-for-vc8") 0037 #endif 0038 0039 #include <algorithm> 0040 #include <cassert> 0041 #include <clocale> 0042 #include <cstdlib> 0043 #include <cmath> 0044 #include <iomanip> 0045 #include <iostream> 0046 #include <limits> 0047 #include <string> 0048 #include <typeinfo> 0049 #include <type_traits> 0050 0051 // Although SQL standard mandates right padding CHAR(N) values to their length 0052 // with spaces, some backends don't confirm to it: 0053 // 0054 // - Firebird does pad the string but to the byte-size (not character size) of 0055 // the column (i.e. CHAR(10) NONE is padded to 10 bytes but CHAR(10) UTF8 -- 0056 // to 40). 0057 // - For MySql PAD_CHAR_TO_FULL_LENGTH option must be set, otherwise the value 0058 // is trimmed. 0059 // - SQLite never behaves correctly at all. 0060 // 0061 // This method will check result string from column defined as fixed char It 0062 // will check only bytes up to the original string size. If padded string is 0063 // bigger than expected string then all remaining chars must be spaces so if 0064 // any non-space character is found it will fail. 0065 void 0066 checkEqualPadded(const std::string& padded_str, const std::string& expected_str) 0067 { 0068 size_t const len = expected_str.length(); 0069 std::string const start_str(padded_str, 0, len); 0070 0071 if (start_str != expected_str) 0072 { 0073 throw soci::soci_error( 0074 "Expected string \"" + expected_str + "\" " 0075 "is different from the padded string \"" + padded_str + "\"" 0076 ); 0077 } 0078 0079 if (padded_str.length() > len) 0080 { 0081 std::string const end_str(padded_str, len); 0082 if (end_str != std::string(padded_str.length() - len, ' ')) 0083 { 0084 throw soci::soci_error( 0085 "\"" + padded_str + "\" starts with \"" + padded_str + 0086 "\" but non-space characater(s) are found aftewards" 0087 ); 0088 } 0089 } 0090 } 0091 0092 #define CHECK_EQUAL_PADDED(padded_str, expected_str) \ 0093 CHECK_NOTHROW(checkEqualPadded(padded_str, expected_str)); 0094 0095 // Objects used later in tests 14,15 0096 struct PhonebookEntry 0097 { 0098 std::string name; 0099 std::string phone; 0100 }; 0101 0102 struct PhonebookEntry2 : public PhonebookEntry 0103 { 0104 }; 0105 0106 class PhonebookEntry3 0107 { 0108 public: 0109 void setName(std::string const & n) { name_ = n; } 0110 std::string getName() const { return name_; } 0111 0112 void setPhone(std::string const & p) { phone_ = p; } 0113 std::string getPhone() const { return phone_; } 0114 0115 public: 0116 std::string name_; 0117 std::string phone_; 0118 }; 0119 0120 // user-defined object for test26 and test28 0121 class MyInt 0122 { 0123 public: 0124 MyInt() : i_() {} 0125 MyInt(int i) : i_(i) {} 0126 void set(int i) { i_ = i; } 0127 int get() const { return i_; } 0128 private: 0129 int i_; 0130 }; 0131 0132 // user-defined object for the "vector of custom type objects" tests. 0133 class MyOptionalString 0134 { 0135 public: 0136 MyOptionalString() : valid_(false) {} 0137 MyOptionalString(const std::string& str) : valid_(true), str_(str) {} 0138 void set(const std::string& str) { valid_ = true; str_ = str; } 0139 void reset() { valid_ = false; str_.clear(); } 0140 bool is_valid() const { return valid_; } 0141 const std::string &get() const { return str_; } 0142 private: 0143 bool valid_; 0144 std::string str_; 0145 }; 0146 0147 std::ostream& operator<<(std::ostream& ostr, const MyOptionalString& optstr) 0148 { 0149 ostr << (optstr.is_valid() ? "\"" + optstr.get() + "\"" : std::string("(null)")); 0150 0151 return ostr; 0152 } 0153 0154 std::ostream& operator<<(std::ostream& ostr, const std::vector<MyOptionalString>& vec) 0155 { 0156 if ( vec.empty() ) 0157 { 0158 ostr << "Empty vector"; 0159 } 0160 else 0161 { 0162 ostr << "Vector of size " << vec.size() << " containing\n"; 0163 for ( size_t n = 0; n < vec.size(); ++n ) 0164 { 0165 ostr << "\t [" << std::setw(3) << n << "] = " << vec[n] << "\n"; 0166 } 0167 } 0168 0169 return ostr; 0170 } 0171 0172 namespace soci 0173 { 0174 0175 // basic type conversion for user-defined type with single base value 0176 template<> struct type_conversion<MyInt> 0177 { 0178 typedef int base_type; 0179 0180 static void from_base(int i, indicator ind, MyInt &mi) 0181 { 0182 if (ind == i_ok) 0183 { 0184 mi.set(i); 0185 } 0186 } 0187 0188 static void to_base(MyInt const &mi, int &i, indicator &ind) 0189 { 0190 i = mi.get(); 0191 ind = i_ok; 0192 } 0193 }; 0194 0195 // basic type conversion for string based user-defined type which can be null 0196 template<> struct type_conversion<MyOptionalString> 0197 { 0198 typedef std::string base_type; 0199 0200 static void from_base(const base_type& in, indicator ind, MyOptionalString& out) 0201 { 0202 if (ind == i_null) 0203 { 0204 out.reset(); 0205 } 0206 else 0207 { 0208 out.set(in); 0209 } 0210 } 0211 0212 static void to_base(const MyOptionalString& in, base_type& out, indicator& ind) 0213 { 0214 if (in.is_valid()) 0215 { 0216 out = in.get(); 0217 ind = i_ok; 0218 } 0219 else 0220 { 0221 ind = i_null; 0222 } 0223 } 0224 }; 0225 0226 // basic type conversion on many values (ORM) 0227 template<> struct type_conversion<PhonebookEntry> 0228 { 0229 typedef soci::values base_type; 0230 0231 static void from_base(values const &v, indicator /* ind */, PhonebookEntry &pe) 0232 { 0233 // here we ignore the possibility the the whole object might be NULL 0234 pe.name = v.get<std::string>("NAME"); 0235 pe.phone = v.get<std::string>("PHONE", "<NULL>"); 0236 } 0237 0238 static void to_base(PhonebookEntry const &pe, values &v, indicator &ind) 0239 { 0240 v.set("NAME", pe.name); 0241 v.set("PHONE", pe.phone, pe.phone.empty() ? i_null : i_ok); 0242 ind = i_ok; 0243 } 0244 }; 0245 0246 // type conversion which directly calls values::get_indicator() 0247 template<> struct type_conversion<PhonebookEntry2> 0248 { 0249 typedef soci::values base_type; 0250 0251 static void from_base(values const &v, indicator /* ind */, PhonebookEntry2 &pe) 0252 { 0253 // here we ignore the possibility the the whole object might be NULL 0254 0255 pe.name = v.get<std::string>("NAME"); 0256 indicator ind = v.get_indicator("PHONE"); //another way to test for null 0257 pe.phone = ind == i_null ? "<NULL>" : v.get<std::string>("PHONE"); 0258 } 0259 0260 static void to_base(PhonebookEntry2 const &pe, values &v, indicator &ind) 0261 { 0262 v.set("NAME", pe.name); 0263 v.set("PHONE", pe.phone, pe.phone.empty() ? i_null : i_ok); 0264 ind = i_ok; 0265 } 0266 }; 0267 0268 template<> struct type_conversion<PhonebookEntry3> 0269 { 0270 typedef soci::values base_type; 0271 0272 static void from_base(values const &v, indicator /* ind */, PhonebookEntry3 &pe) 0273 { 0274 // here we ignore the possibility the the whole object might be NULL 0275 0276 pe.setName(v.get<std::string>("NAME")); 0277 pe.setPhone(v.get<std::string>("PHONE", "<NULL>")); 0278 } 0279 0280 static void to_base(PhonebookEntry3 const &pe, values &v, indicator &ind) 0281 { 0282 v.set("NAME", pe.getName()); 0283 v.set("PHONE", pe.getPhone(), pe.getPhone().empty() ? i_null : i_ok); 0284 ind = i_ok; 0285 } 0286 }; 0287 0288 } // namespace soci 0289 0290 namespace soci 0291 { 0292 namespace tests 0293 { 0294 0295 // TODO: improve cleanup capabilities by subtypes, soci_test name may be omitted --mloskot 0296 // i.e. optional ctor param accepting custom table name 0297 class table_creator_base 0298 { 0299 public: 0300 table_creator_base(session& sql) 0301 : msession(sql) { drop(); } 0302 0303 virtual ~table_creator_base() { drop();} 0304 private: 0305 void drop() 0306 { 0307 try 0308 { 0309 msession << "drop table soci_test"; 0310 } 0311 catch (soci_error const& e) 0312 { 0313 //std::cerr << e.what() << std::endl; 0314 e.what(); 0315 } 0316 } 0317 session& msession; 0318 0319 SOCI_NOT_COPYABLE(table_creator_base) 0320 }; 0321 0322 class procedure_creator_base 0323 { 0324 public: 0325 procedure_creator_base(session& sql) 0326 : msession(sql) { drop(); } 0327 0328 virtual ~procedure_creator_base() { drop();} 0329 private: 0330 void drop() 0331 { 0332 try { msession << "drop procedure soci_test"; } catch (soci_error&) {} 0333 } 0334 session& msession; 0335 0336 SOCI_NOT_COPYABLE(procedure_creator_base) 0337 }; 0338 0339 class function_creator_base 0340 { 0341 public: 0342 function_creator_base(session& sql) 0343 : msession(sql) { drop(); } 0344 0345 virtual ~function_creator_base() { drop();} 0346 0347 protected: 0348 virtual std::string dropstatement() 0349 { 0350 return "drop function soci_test"; 0351 } 0352 0353 private: 0354 void drop() 0355 { 0356 try { msession << dropstatement(); } catch (soci_error&) {} 0357 } 0358 session& msession; 0359 0360 SOCI_NOT_COPYABLE(function_creator_base) 0361 }; 0362 0363 // This is a singleton class, at any given time there is at most one test 0364 // context alive and common_tests fixture class uses it. 0365 class test_context_base 0366 { 0367 public: 0368 test_context_base(backend_factory const &backEnd, 0369 std::string const &connectString) 0370 : backEndFactory_(backEnd), 0371 connectString_(connectString) 0372 { 0373 // This can't be a CHECK() because the test context is constructed 0374 // outside of any test. 0375 assert(!the_test_context_); 0376 0377 the_test_context_ = this; 0378 0379 // To allow running tests in non-default ("C") locale, the following 0380 // environment variable can be set and then the current default locale 0381 // (which can itself be changed by setting LC_ALL environment variable) 0382 // will then be used. 0383 if (std::getenv("SOCI_TEST_USE_LC_ALL")) 0384 std::setlocale(LC_ALL, ""); 0385 } 0386 0387 static test_context_base const& get_instance() 0388 { 0389 REQUIRE(the_test_context_); 0390 0391 return *the_test_context_; 0392 } 0393 0394 backend_factory const & get_backend_factory() const 0395 { 0396 return backEndFactory_; 0397 } 0398 0399 std::string get_connect_string() const 0400 { 0401 return connectString_; 0402 } 0403 0404 virtual std::string to_date_time(std::string const &dateTime) const = 0; 0405 0406 virtual table_creator_base* table_creator_1(session&) const = 0; 0407 virtual table_creator_base* table_creator_2(session&) const = 0; 0408 virtual table_creator_base* table_creator_3(session&) const = 0; 0409 virtual table_creator_base* table_creator_4(session&) const = 0; 0410 0411 // Override this to return the table creator for a simple table containing 0412 // an integer "id" column and CLOB "s" one. 0413 // 0414 // Returns null by default to indicate that CLOB is not supported. 0415 virtual table_creator_base* table_creator_clob(session&) const { return NULL; } 0416 0417 // Override this to return the table creator for a simple table containing 0418 // an integer "id" column and XML "x" one. 0419 // 0420 // Returns null by default to indicate that XML is not supported. 0421 virtual table_creator_base* table_creator_xml(session&) const { return NULL; } 0422 0423 // Override this to return the table creator for a simple table containing 0424 // an identity integer "id" and a simple integer "val" columns. 0425 // 0426 // Returns null by default to indicate that identity is not supported. 0427 virtual table_creator_base* table_creator_get_last_insert_id(session&) const { return NULL; } 0428 0429 // Return the casts that must be used to convert the between the database 0430 // XML type and the query parameters. 0431 // 0432 // By default no special casts are done. 0433 virtual std::string to_xml(std::string const& x) const { return x; } 0434 virtual std::string from_xml(std::string const& x) const { return x; } 0435 0436 // Override this if the backend not only supports working with XML values 0437 // (and so returns a non-null value from table_creator_xml()), but the 0438 // database itself has real XML support instead of just allowing to store 0439 // and retrieve XML as text. "Real" support means at least preventing the 0440 // application from storing malformed XML in the database. 0441 virtual bool has_real_xml_support() const { return false; } 0442 0443 // Override this if the backend doesn't handle floating point values 0444 // correctly, i.e. writing a value and reading it back doesn't return 0445 // *exactly* the same value. 0446 virtual bool has_fp_bug() const { return false; } 0447 0448 // Override this if the backend wrongly returns CR LF when reading a string 0449 // with just LFs from the database to strip the unwanted CRs. 0450 virtual std::string fix_crlf_if_necessary(std::string const& s) const { return s; } 0451 0452 // Override this if the backend doesn't handle multiple active select 0453 // statements at the same time, i.e. a result set must be entirely consumed 0454 // before creating a new one (this is the case of MS SQL without MARS). 0455 virtual bool has_multiple_select_bug() const { return false; } 0456 0457 // Override this if the backend may not have transactions support. 0458 virtual bool has_transactions_support(session&) const { return true; } 0459 0460 // Override this if the backend silently truncates string values too long 0461 // to fit by default. 0462 virtual bool has_silent_truncate_bug(session&) const { return false; } 0463 0464 // Override this if the backend doesn't distinguish between empty and null 0465 // strings (Oracle does this). 0466 virtual bool treats_empty_strings_as_null() const { return false; } 0467 0468 // Override this to call commit() if it's necessary for the DDL statements 0469 // to be taken into account (currently this is only the case for Firebird). 0470 virtual void on_after_ddl(session&) const { } 0471 0472 // Put the database in SQL-complient mode for CHAR(N) values, return false 0473 // if it's impossible, i.e. if the database doesn't behave correctly 0474 // whatever we do. 0475 virtual bool enable_std_char_padding(session&) const { return true; } 0476 0477 // Return the SQL expression giving the length of the specified string, 0478 // i.e. "char_length(s)" in standard SQL but often "len(s)" or "length(s)" 0479 // in practice and sometimes even worse (thanks Oracle). 0480 virtual std::string sql_length(std::string const& s) const = 0; 0481 0482 virtual ~test_context_base() 0483 { 0484 the_test_context_ = NULL; 0485 } 0486 0487 private: 0488 backend_factory const &backEndFactory_; 0489 std::string const connectString_; 0490 0491 static test_context_base* the_test_context_; 0492 0493 SOCI_NOT_COPYABLE(test_context_base) 0494 }; 0495 0496 // Currently all tests consist of just a single source file, so we can define 0497 // this member here because this header is included exactly once. 0498 tests::test_context_base* tests::test_context_base::the_test_context_ = NULL; 0499 0500 0501 // Compare doubles for approximate equality. This has to be used everywhere 0502 // where we write "3.14" (or "6.28") to the database as a string and then 0503 // compare the value read back with the literal 3.14 floating point constant 0504 // because they are not the same. 0505 // 0506 // It is also used for the backends which currently don't handle doubles 0507 // correctly. 0508 // 0509 // Notice that this function is normally not used directly but rather from the 0510 // macro below. 0511 inline bool are_doubles_approx_equal(double const a, double const b) 0512 { 0513 // The formula taken from CATCH test framework 0514 // https://github.com/philsquared/Catch/ 0515 // Thanks to Richard Harris for his help refining this formula 0516 double const epsilon(std::numeric_limits<float>::epsilon() * 100); 0517 double const scale(1.0); 0518 return std::fabs(a - b) < epsilon * (scale + (std::max)(std::fabs(a), std::fabs(b))); 0519 } 0520 0521 // This is a macro to ensure we use the correct line numbers. The weird 0522 // do/while construction is used to make this a statement and the even weirder 0523 // condition in while ensures that the loop is executed exactly once without 0524 // triggering warnings from MSVC about the condition being always false. 0525 #define ASSERT_EQUAL_APPROX(a, b) \ 0526 do { \ 0527 if (!are_doubles_approx_equal((a), (b))) { \ 0528 FAIL( "Approximate equality check failed: " \ 0529 << std::fixed \ 0530 << std::setprecision(std::numeric_limits<double>::digits10 + 1) \ 0531 << (a) << " != " << (b) ); \ 0532 } \ 0533 } while ( (void)0, 0 ) 0534 0535 0536 // Exact double comparison function. We need one, instead of writing "a == b", 0537 // only in order to have some place to put the pragmas disabling gcc warnings. 0538 inline bool 0539 are_doubles_exactly_equal(double a, double b) 0540 { 0541 // Avoid g++ warnings: we do really want the exact equality here. 0542 SOCI_GCC_WARNING_SUPPRESS(float-equal) 0543 0544 return a == b; 0545 0546 SOCI_GCC_WARNING_RESTORE(float-equal) 0547 } 0548 0549 #define ASSERT_EQUAL_EXACT(a, b) \ 0550 do { \ 0551 if (!are_doubles_exactly_equal((a), (b))) { \ 0552 FAIL( "Exact equality check failed: " \ 0553 << std::fixed \ 0554 << std::setprecision(std::numeric_limits<double>::digits10 + 1) \ 0555 << (a) << " != " << (b) ); \ 0556 } \ 0557 } while ( (void)0, 0 ) 0558 0559 0560 // Compare two floating point numbers either exactly or approximately depending 0561 // on test_context::has_fp_bug() return value. 0562 inline bool 0563 are_doubles_equal(test_context_base const& tc, double a, double b) 0564 { 0565 return tc.has_fp_bug() 0566 ? are_doubles_approx_equal(a, b) 0567 : are_doubles_exactly_equal(a, b); 0568 } 0569 0570 // This macro should be used when where we don't have any problems with string 0571 // literals vs floating point literals mismatches described above and would 0572 // ideally compare the numbers exactly but, unfortunately, currently can't do 0573 // this unconditionally because at least some backends are currently buggy and 0574 // don't handle the floating point values correctly. 0575 // 0576 // This can be only used from inside the common_tests class as it relies on 0577 // having an accessible "tc_" variable to determine whether exact or 0578 // approximate comparison should be used. 0579 #define ASSERT_EQUAL(a, b) \ 0580 do { \ 0581 if (!are_doubles_equal(tc_, (a), (b))) { \ 0582 FAIL( "Equality check failed: " \ 0583 << std::fixed \ 0584 << std::setprecision(std::numeric_limits<double>::digits10 + 1) \ 0585 << (a) << " != " << (b) ); \ 0586 } \ 0587 } while ( (void)0, 0 ) 0588 0589 0590 class common_tests 0591 { 0592 public: 0593 common_tests() 0594 : tc_(test_context_base::get_instance()), 0595 backEndFactory_(tc_.get_backend_factory()), 0596 connectString_(tc_.get_connect_string()) 0597 {} 0598 0599 protected: 0600 test_context_base const & tc_; 0601 backend_factory const &backEndFactory_; 0602 std::string const connectString_; 0603 0604 SOCI_NOT_COPYABLE(common_tests) 0605 }; 0606 0607 using auto_table_creator = std::unique_ptr<table_creator_base>; 0608 0609 // Define the test cases in their own namespace to avoid clashes with the test 0610 // cases defined in individual backend tests: as only line number is used for 0611 // building the name of the "anonymous" function by the TEST_CASE macro, we 0612 // could have a conflict between a test defined here and in some backend if 0613 // they happened to start on the same line. 0614 namespace test_cases 0615 { 0616 0617 TEST_CASE_METHOD(common_tests, "Exception on not connected", "[core][exception]") 0618 { 0619 soci::session sql; // no connection 0620 0621 // ensure connection is checked, no crash occurs 0622 CHECK_THROWS_AS(sql.begin(), soci_error); 0623 CHECK_THROWS_AS(sql.commit(), soci_error); 0624 CHECK_THROWS_AS(sql.rollback(), soci_error); 0625 CHECK_THROWS_AS(sql.get_backend_name(), soci_error); 0626 CHECK_THROWS_AS(sql.make_statement_backend(), soci_error); 0627 CHECK_THROWS_AS(sql.make_rowid_backend(), soci_error); 0628 CHECK_THROWS_AS(sql.make_blob_backend(), soci_error); 0629 0630 std::string s; 0631 long long l; 0632 CHECK_THROWS_AS(sql.get_next_sequence_value(s, l), soci_error); 0633 CHECK_THROWS_AS(sql.get_last_insert_id(s, l), soci_error); 0634 } 0635 0636 TEST_CASE_METHOD(common_tests, "Basic functionality", "[core][basics]") 0637 { 0638 soci::session sql(backEndFactory_, connectString_); 0639 0640 auto_table_creator tableCreator(tc_.table_creator_1(sql)); 0641 0642 CHECK_THROWS_AS(sql << "drop table soci_test_nosuchtable", soci_error); 0643 0644 sql << "insert into soci_test (id) values (" << 123 << ")"; 0645 int id; 0646 sql << "select id from soci_test", into(id); 0647 CHECK(id == 123); 0648 0649 sql << "insert into soci_test (id) values (" << 234 << ")"; 0650 sql << "insert into soci_test (id) values (" << 345 << ")"; 0651 // Test prepare, execute, fetch correctness 0652 statement st = (sql.prepare << "select id from soci_test", into(id)); 0653 st.execute(); 0654 int count = 0; 0655 while(st.fetch()) 0656 count++; 0657 CHECK(count == 3 ); 0658 bool fetchEnd = st.fetch(); // All the data has been read here so additional fetch must return false 0659 CHECK(fetchEnd == false); 0660 } 0661 0662 // "into" tests, type conversions, etc. 0663 TEST_CASE_METHOD(common_tests, "Use and into", "[core][into]") 0664 { 0665 soci::session sql(backEndFactory_, connectString_); 0666 0667 auto_table_creator tableCreator(tc_.table_creator_1(sql)); 0668 0669 SECTION("Round trip works for char") 0670 { 0671 char c('a'); 0672 sql << "insert into soci_test(c) values(:c)", use(c); 0673 sql << "select c from soci_test", into(c); 0674 CHECK(c == 'a'); 0675 } 0676 0677 SECTION("Round trip works for string") 0678 { 0679 std::string helloSOCI("Hello, SOCI!"); 0680 sql << "insert into soci_test(str) values(:s)", use(helloSOCI); 0681 std::string str; 0682 sql << "select str from soci_test", into(str); 0683 CHECK(str == "Hello, SOCI!"); 0684 } 0685 0686 SECTION("Round trip works for short") 0687 { 0688 short three(3); 0689 sql << "insert into soci_test(sh) values(:id)", use(three); 0690 short sh(0); 0691 sql << "select sh from soci_test", into(sh); 0692 CHECK(sh == 3); 0693 } 0694 0695 SECTION("Round trip works for int") 0696 { 0697 int five(5); 0698 sql << "insert into soci_test(id) values(:id)", use(five); 0699 int i(0); 0700 sql << "select id from soci_test", into(i); 0701 CHECK(i == 5); 0702 } 0703 0704 SECTION("Round trip works for unsigned long") 0705 { 0706 unsigned long seven(7); 0707 sql << "insert into soci_test(ul) values(:ul)", use(seven); 0708 unsigned long ul(0); 0709 sql << "select ul from soci_test", into(ul); 0710 CHECK(ul == 7); 0711 } 0712 0713 SECTION("Round trip works for double") 0714 { 0715 double pi(3.14159265); 0716 sql << "insert into soci_test(d) values(:d)", use(pi); 0717 double d(0.0); 0718 sql << "select d from soci_test", into(d); 0719 ASSERT_EQUAL(d, pi); 0720 } 0721 0722 SECTION("Round trip works for date without time") 0723 { 0724 std::tm nov15 = std::tm(); 0725 nov15.tm_year = 105; 0726 nov15.tm_mon = 10; 0727 nov15.tm_mday = 15; 0728 nov15.tm_hour = 0; 0729 nov15.tm_min = 0; 0730 nov15.tm_sec = 0; 0731 0732 sql << "insert into soci_test(tm) values(:tm)", use(nov15); 0733 0734 std::tm t = std::tm(); 0735 sql << "select tm from soci_test", into(t); 0736 CHECK(t.tm_year == 105); 0737 CHECK(t.tm_mon == 10); 0738 CHECK(t.tm_mday == 15); 0739 CHECK(t.tm_hour == 0); 0740 CHECK(t.tm_min == 0); 0741 CHECK(t.tm_sec == 0); 0742 } 0743 0744 SECTION("Round trip works for date with time") 0745 { 0746 std::tm nov15 = std::tm(); 0747 nov15.tm_year = 105; 0748 nov15.tm_mon = 10; 0749 nov15.tm_mday = 15; 0750 nov15.tm_hour = 22; 0751 nov15.tm_min = 14; 0752 nov15.tm_sec = 17; 0753 0754 sql << "insert into soci_test(tm) values(:tm)", use(nov15); 0755 0756 std::tm t = std::tm(); 0757 sql << "select tm from soci_test", into(t); 0758 CHECK(t.tm_year == 105); 0759 CHECK(t.tm_mon == 10); 0760 CHECK(t.tm_mday == 15); 0761 CHECK(t.tm_hour == 22); 0762 CHECK(t.tm_min == 14); 0763 CHECK(t.tm_sec == 17); 0764 } 0765 0766 SECTION("Indicator is filled correctly in the simplest case") 0767 { 0768 int id(1); 0769 std::string str("Hello"); 0770 sql << "insert into soci_test(id, str) values(:id, :str)", 0771 use(id), use(str); 0772 0773 int i; 0774 indicator ind; 0775 sql << "select id from soci_test", into(i, ind); 0776 CHECK(ind == i_ok); 0777 } 0778 0779 SECTION("Indicators work correctly more generally") 0780 { 0781 sql << "insert into soci_test(id,tm) values(NULL,NULL)"; 0782 int i; 0783 indicator ind; 0784 sql << "select id from soci_test", into(i, ind); 0785 CHECK(ind == i_null); 0786 0787 // additional test for NULL with std::tm 0788 std::tm t = std::tm(); 0789 sql << "select tm from soci_test", into(t, ind); 0790 CHECK(ind == i_null); 0791 0792 // indicator should be initialized even when nothing is found 0793 ind = i_ok; 0794 sql << "select id from soci_test where str='NO SUCH ROW'", 0795 into(i, ind); 0796 CHECK(ind == i_null); 0797 0798 try 0799 { 0800 // expect error 0801 sql << "select id from soci_test", into(i); 0802 FAIL("expected exception not thrown"); 0803 } 0804 catch (soci_error const &e) 0805 { 0806 CHECK(e.get_error_message() == 0807 "Null value fetched and no indicator defined."); 0808 CHECK_THAT(e.what(), 0809 Catch::Contains("for the parameter number 1")); 0810 } 0811 0812 sql << "select id from soci_test where id = 1000", into(i, ind); 0813 CHECK(sql.got_data() == false); 0814 0815 // no data expected 0816 sql << "select id from soci_test where id = 1000", into(i); 0817 CHECK(sql.got_data() == false); 0818 0819 // no data expected, test correct behaviour with use 0820 int id = 1000; 0821 sql << "select id from soci_test where id = :id", use(id), into(i); 0822 CHECK(sql.got_data() == false); 0823 } 0824 } 0825 0826 // repeated fetch and bulk fetch 0827 TEST_CASE_METHOD(common_tests, "Repeated and bulk fetch", "[core][bulk]") 0828 { 0829 soci::session sql(backEndFactory_, connectString_); 0830 0831 // create and populate the test table 0832 auto_table_creator tableCreator(tc_.table_creator_1(sql)); 0833 0834 SECTION("char") 0835 { 0836 char c; 0837 for (c = 'a'; c <= 'z'; ++c) 0838 { 0839 sql << "insert into soci_test(c) values(\'" << c << "\')"; 0840 } 0841 0842 int count; 0843 sql << "select count(*) from soci_test", into(count); 0844 CHECK(count == 'z' - 'a' + 1); 0845 0846 { 0847 char c2 = 'a'; 0848 0849 statement st = (sql.prepare << 0850 "select c from soci_test order by c", into(c)); 0851 0852 st.execute(); 0853 while (st.fetch()) 0854 { 0855 CHECK(c == c2); 0856 ++c2; 0857 } 0858 CHECK(c2 == 'a' + count); 0859 } 0860 { 0861 char c2 = 'a'; 0862 0863 std::vector<char> vec(10); 0864 statement st = (sql.prepare << 0865 "select c from soci_test order by c", into(vec)); 0866 st.execute(); 0867 while (st.fetch()) 0868 { 0869 for (std::size_t i = 0; i != vec.size(); ++i) 0870 { 0871 CHECK(c2 == vec[i]); 0872 ++c2; 0873 } 0874 0875 vec.resize(10); 0876 } 0877 CHECK(c2 == 'a' + count); 0878 } 0879 0880 { 0881 // verify an exception is thrown when empty vector is used 0882 std::vector<char> vec; 0883 try 0884 { 0885 sql << "select c from soci_test", into(vec); 0886 FAIL("expected exception not thrown"); 0887 } 0888 catch (soci_error const &e) 0889 { 0890 CHECK(e.get_error_message() == 0891 "Vectors of size 0 are not allowed."); 0892 } 0893 } 0894 0895 } 0896 0897 // repeated fetch and bulk fetch of std::string 0898 SECTION("std::string") 0899 { 0900 int const rowsToTest = 10; 0901 for (int i = 0; i != rowsToTest; ++i) 0902 { 0903 std::ostringstream ss; 0904 ss << "Hello_" << i; 0905 0906 sql << "insert into soci_test(str) values(\'" 0907 << ss.str() << "\')"; 0908 } 0909 0910 int count; 0911 sql << "select count(*) from soci_test", into(count); 0912 CHECK(count == rowsToTest); 0913 0914 { 0915 int i = 0; 0916 std::string s; 0917 statement st = (sql.prepare << 0918 "select str from soci_test order by str", into(s)); 0919 0920 st.execute(); 0921 while (st.fetch()) 0922 { 0923 std::ostringstream ss; 0924 ss << "Hello_" << i; 0925 CHECK(s == ss.str()); 0926 ++i; 0927 } 0928 CHECK(i == rowsToTest); 0929 } 0930 { 0931 int i = 0; 0932 0933 std::vector<std::string> vec(4); 0934 statement st = (sql.prepare << 0935 "select str from soci_test order by str", into(vec)); 0936 st.execute(); 0937 while (st.fetch()) 0938 { 0939 for (std::size_t j = 0; j != vec.size(); ++j) 0940 { 0941 std::ostringstream ss; 0942 ss << "Hello_" << i; 0943 CHECK(ss.str() == vec[j]); 0944 ++i; 0945 } 0946 0947 vec.resize(4); 0948 } 0949 CHECK(i == rowsToTest); 0950 } 0951 } 0952 0953 SECTION("short") 0954 { 0955 short const rowsToTest = 100; 0956 short sh; 0957 for (sh = 0; sh != rowsToTest; ++sh) 0958 { 0959 sql << "insert into soci_test(sh) values(" << sh << ")"; 0960 } 0961 0962 int count; 0963 sql << "select count(*) from soci_test", into(count); 0964 CHECK(count == rowsToTest); 0965 0966 { 0967 short sh2 = 0; 0968 0969 statement st = (sql.prepare << 0970 "select sh from soci_test order by sh", into(sh)); 0971 0972 st.execute(); 0973 while (st.fetch()) 0974 { 0975 CHECK(sh == sh2); 0976 ++sh2; 0977 } 0978 CHECK(sh2 == rowsToTest); 0979 } 0980 { 0981 short sh2 = 0; 0982 0983 std::vector<short> vec(8); 0984 statement st = (sql.prepare << 0985 "select sh from soci_test order by sh", into(vec)); 0986 st.execute(); 0987 while (st.fetch()) 0988 { 0989 for (std::size_t i = 0; i != vec.size(); ++i) 0990 { 0991 CHECK(sh2 == vec[i]); 0992 ++sh2; 0993 } 0994 0995 vec.resize(8); 0996 } 0997 CHECK(sh2 == rowsToTest); 0998 } 0999 } 1000 1001 SECTION("int") 1002 { 1003 int const rowsToTest = 100; 1004 int i; 1005 for (i = 0; i != rowsToTest; ++i) 1006 { 1007 sql << "insert into soci_test(id) values(" << i << ")"; 1008 } 1009 1010 int count; 1011 sql << "select count(*) from soci_test", into(count); 1012 CHECK(count == rowsToTest); 1013 1014 { 1015 int i2 = 0; 1016 1017 statement st = (sql.prepare << 1018 "select id from soci_test order by id", into(i)); 1019 1020 st.execute(); 1021 while (st.fetch()) 1022 { 1023 CHECK(i == i2); 1024 ++i2; 1025 } 1026 CHECK(i2 == rowsToTest); 1027 } 1028 { 1029 // additional test with the use element 1030 1031 int i2 = 0; 1032 int cond = 0; // this condition is always true 1033 1034 statement st = (sql.prepare << 1035 "select id from soci_test where id >= :cond order by id", 1036 use(cond), into(i)); 1037 1038 st.execute(); 1039 while (st.fetch()) 1040 { 1041 CHECK(i == i2); 1042 ++i2; 1043 } 1044 CHECK(i2 == rowsToTest); 1045 } 1046 { 1047 int i2 = 0; 1048 1049 std::vector<int> vec(8); 1050 statement st = (sql.prepare << 1051 "select id from soci_test order by id", into(vec)); 1052 st.execute(); 1053 while (st.fetch()) 1054 { 1055 for (std::size_t n = 0; n != vec.size(); ++n) 1056 { 1057 CHECK(i2 == vec[n]); 1058 ++i2; 1059 } 1060 1061 vec.resize(8); 1062 } 1063 CHECK(i2 == rowsToTest); 1064 } 1065 } 1066 1067 SECTION("unsigned int") 1068 { 1069 unsigned int const rowsToTest = 100; 1070 unsigned int ul; 1071 for (ul = 0; ul != rowsToTest; ++ul) 1072 { 1073 sql << "insert into soci_test(ul) values(" << ul << ")"; 1074 } 1075 1076 int count; 1077 sql << "select count(*) from soci_test", into(count); 1078 CHECK(count == static_cast<int>(rowsToTest)); 1079 1080 { 1081 unsigned int ul2 = 0; 1082 1083 statement st = (sql.prepare << 1084 "select ul from soci_test order by ul", into(ul)); 1085 1086 st.execute(); 1087 while (st.fetch()) 1088 { 1089 CHECK(ul == ul2); 1090 ++ul2; 1091 } 1092 CHECK(ul2 == rowsToTest); 1093 } 1094 { 1095 unsigned int ul2 = 0; 1096 1097 std::vector<unsigned int> vec(8); 1098 statement st = (sql.prepare << 1099 "select ul from soci_test order by ul", into(vec)); 1100 st.execute(); 1101 while (st.fetch()) 1102 { 1103 for (std::size_t i = 0; i != vec.size(); ++i) 1104 { 1105 CHECK(ul2 == vec[i]); 1106 ++ul2; 1107 } 1108 1109 vec.resize(8); 1110 } 1111 CHECK(ul2 == rowsToTest); 1112 } 1113 } 1114 1115 SECTION("unsigned long long") 1116 { 1117 unsigned int const rowsToTest = 100; 1118 unsigned long long ul; 1119 for (ul = 0; ul != rowsToTest; ++ul) 1120 { 1121 sql << "insert into soci_test(ul) values(" << ul << ")"; 1122 } 1123 1124 int count; 1125 sql << "select count(*) from soci_test", into(count); 1126 CHECK(count == static_cast<int>(rowsToTest)); 1127 1128 { 1129 unsigned long long ul2 = 0; 1130 1131 statement st = (sql.prepare << 1132 "select ul from soci_test order by ul", into(ul)); 1133 1134 st.execute(); 1135 while (st.fetch()) 1136 { 1137 CHECK(ul == ul2); 1138 ++ul2; 1139 } 1140 CHECK(ul2 == rowsToTest); 1141 } 1142 { 1143 unsigned long long ul2 = 0; 1144 1145 std::vector<unsigned long long> vec(8); 1146 statement st = (sql.prepare << 1147 "select ul from soci_test order by ul", into(vec)); 1148 st.execute(); 1149 while (st.fetch()) 1150 { 1151 for (std::size_t i = 0; i != vec.size(); ++i) 1152 { 1153 CHECK(ul2 == vec[i]); 1154 ++ul2; 1155 } 1156 1157 vec.resize(8); 1158 } 1159 CHECK(ul2 == rowsToTest); 1160 } 1161 } 1162 1163 SECTION("double") 1164 { 1165 int const rowsToTest = 100; 1166 double d = 0.0; 1167 1168 statement sti = (sql.prepare << 1169 "insert into soci_test(d) values(:d)", use(d)); 1170 for (int i = 0; i != rowsToTest; ++i) 1171 { 1172 sti.execute(true); 1173 d += 0.6; 1174 } 1175 1176 int count; 1177 sql << "select count(*) from soci_test", into(count); 1178 CHECK(count == rowsToTest); 1179 1180 { 1181 double d2 = 0.0; 1182 int i = 0; 1183 1184 statement st = (sql.prepare << 1185 "select d from soci_test order by d", into(d)); 1186 1187 st.execute(); 1188 while (st.fetch()) 1189 { 1190 ASSERT_EQUAL(d, d2); 1191 d2 += 0.6; 1192 ++i; 1193 } 1194 CHECK(i == rowsToTest); 1195 } 1196 { 1197 double d2 = 0.0; 1198 int i = 0; 1199 1200 std::vector<double> vec(8); 1201 statement st = (sql.prepare << 1202 "select d from soci_test order by d", into(vec)); 1203 st.execute(); 1204 while (st.fetch()) 1205 { 1206 for (std::size_t j = 0; j != vec.size(); ++j) 1207 { 1208 ASSERT_EQUAL(d2, vec[j]); 1209 d2 += 0.6; 1210 ++i; 1211 } 1212 1213 vec.resize(8); 1214 } 1215 CHECK(i == rowsToTest); 1216 } 1217 } 1218 1219 SECTION("std::tm") 1220 { 1221 int const rowsToTest = 8; 1222 for (int i = 0; i != rowsToTest; ++i) 1223 { 1224 std::ostringstream ss; 1225 ss << 2000 + i << "-0" << 1 + i << '-' << 20 - i << ' ' 1226 << 15 + i << ':' << 50 - i << ':' << 40 + i; 1227 1228 sql << "insert into soci_test(id, tm) values(" << i 1229 << ", " << tc_.to_date_time(ss.str()) << ")"; 1230 } 1231 1232 int count; 1233 sql << "select count(*) from soci_test", into(count); 1234 CHECK(count == rowsToTest); 1235 1236 { 1237 std::tm t = std::tm(); 1238 int i = 0; 1239 1240 statement st = (sql.prepare << 1241 "select tm from soci_test order by id", into(t)); 1242 1243 st.execute(); 1244 while (st.fetch()) 1245 { 1246 CHECK(t.tm_year == 2000 - 1900 + i); 1247 CHECK(t.tm_mon == i); 1248 CHECK(t.tm_mday == 20 - i); 1249 CHECK(t.tm_hour == 15 + i); 1250 CHECK(t.tm_min == 50 - i); 1251 CHECK(t.tm_sec == 40 + i); 1252 1253 ++i; 1254 } 1255 CHECK(i == rowsToTest); 1256 } 1257 { 1258 int i = 0; 1259 1260 std::vector<std::tm> vec(3); 1261 statement st = (sql.prepare << 1262 "select tm from soci_test order by id", into(vec)); 1263 st.execute(); 1264 while (st.fetch()) 1265 { 1266 for (std::size_t j = 0; j != vec.size(); ++j) 1267 { 1268 CHECK(vec[j].tm_year == 2000 - 1900 + i); 1269 CHECK(vec[j].tm_mon == i); 1270 CHECK(vec[j].tm_mday == 20 - i); 1271 CHECK(vec[j].tm_hour == 15 + i); 1272 CHECK(vec[j].tm_min == 50 - i); 1273 CHECK(vec[j].tm_sec == 40 + i); 1274 1275 ++i; 1276 } 1277 1278 vec.resize(3); 1279 } 1280 CHECK(i == rowsToTest); 1281 } 1282 } 1283 } 1284 1285 // test for indicators (repeated fetch and bulk) 1286 TEST_CASE_METHOD(common_tests, "Indicators", "[core][indicator]") 1287 { 1288 soci::session sql(backEndFactory_, connectString_); 1289 1290 // create and populate the test table 1291 auto_table_creator tableCreator(tc_.table_creator_1(sql)); 1292 { 1293 sql << "insert into soci_test(id, val) values(1, 10)"; 1294 sql << "insert into soci_test(id, val) values(2, 11)"; 1295 sql << "insert into soci_test(id, val) values(3, NULL)"; 1296 sql << "insert into soci_test(id, val) values(4, NULL)"; 1297 sql << "insert into soci_test(id, val) values(5, 12)"; 1298 1299 { 1300 int val; 1301 indicator ind; 1302 1303 statement st = (sql.prepare << 1304 "select val from soci_test order by id", into(val, ind)); 1305 1306 st.execute(); 1307 bool gotData = st.fetch(); 1308 CHECK(gotData); 1309 CHECK(ind == i_ok); 1310 CHECK(val == 10); 1311 gotData = st.fetch(); 1312 CHECK(gotData); 1313 CHECK(ind == i_ok); 1314 CHECK(val == 11); 1315 gotData = st.fetch(); 1316 CHECK(gotData); 1317 CHECK(ind == i_null); 1318 gotData = st.fetch(); 1319 CHECK(gotData); 1320 CHECK(ind == i_null); 1321 gotData = st.fetch(); 1322 CHECK(gotData); 1323 CHECK(ind == i_ok); 1324 CHECK(val == 12); 1325 gotData = st.fetch(); 1326 CHECK(gotData == false); 1327 } 1328 { 1329 std::vector<int> vals(3); 1330 std::vector<indicator> inds(3); 1331 1332 statement st = (sql.prepare << 1333 "select val from soci_test order by id", into(vals, inds)); 1334 1335 st.execute(); 1336 bool gotData = st.fetch(); 1337 CHECK(gotData); 1338 CHECK(vals.size() == 3); 1339 CHECK(inds.size() == 3); 1340 CHECK(inds[0] == i_ok); 1341 CHECK(vals[0] == 10); 1342 CHECK(inds[1] == i_ok); 1343 CHECK(vals[1] == 11); 1344 CHECK(inds[2] == i_null); 1345 gotData = st.fetch(); 1346 CHECK(gotData); 1347 CHECK(vals.size() == 2); 1348 CHECK(inds[0] == i_null); 1349 CHECK(inds[1] == i_ok); 1350 CHECK(vals[1] == 12); 1351 gotData = st.fetch(); 1352 CHECK(gotData == false); 1353 } 1354 1355 // additional test for "no data" condition 1356 { 1357 std::vector<int> vals(3); 1358 std::vector<indicator> inds(3); 1359 1360 statement st = (sql.prepare << 1361 "select val from soci_test where 0 = 1", into(vals, inds)); 1362 1363 bool gotData = st.execute(true); 1364 CHECK(gotData == false); 1365 1366 // for convenience, vectors should be truncated 1367 CHECK(vals.empty()); 1368 CHECK(inds.empty()); 1369 1370 // for even more convenience, fetch should not fail 1371 // but just report end of rowset 1372 // (and vectors should be truncated) 1373 1374 vals.resize(1); 1375 inds.resize(1); 1376 1377 gotData = st.fetch(); 1378 CHECK(gotData == false); 1379 CHECK(vals.empty()); 1380 CHECK(inds.empty()); 1381 } 1382 1383 // additional test for "no data" without prepared statement 1384 { 1385 std::vector<int> vals(3); 1386 std::vector<indicator> inds(3); 1387 1388 sql << "select val from soci_test where 0 = 1", 1389 into(vals, inds); 1390 1391 // vectors should be truncated 1392 CHECK(vals.empty()); 1393 CHECK(inds.empty()); 1394 } 1395 } 1396 1397 } 1398 1399 // test for different sizes of data vector and indicators vector 1400 // (library should force ind. vector to have same size as data vector) 1401 TEST_CASE_METHOD(common_tests, "Indicators vector", "[core][indicator][vector]") 1402 { 1403 soci::session sql(backEndFactory_, connectString_); 1404 1405 // create and populate the test table 1406 auto_table_creator tableCreator(tc_.table_creator_1(sql)); 1407 { 1408 sql << "insert into soci_test(id, str, val) values(1, 'ten', 10)"; 1409 sql << "insert into soci_test(id, str, val) values(2, 'elf', 11)"; 1410 sql << "insert into soci_test(id, str, val) values(3, NULL, NULL)"; 1411 sql << "insert into soci_test(id, str, val) values(4, NULL, NULL)"; 1412 sql << "insert into soci_test(id, str, val) values(5, 'xii', 12)"; 1413 1414 { 1415 std::vector<int> vals(4); 1416 std::vector<indicator> inds; 1417 1418 statement st = (sql.prepare << 1419 "select val from soci_test order by id", into(vals, inds)); 1420 1421 st.execute(); 1422 st.fetch(); 1423 CHECK(vals.size() == 4); 1424 CHECK(inds.size() == 4); 1425 vals.resize(3); 1426 st.fetch(); 1427 CHECK(vals.size() == 1); 1428 CHECK(inds.size() == 1); 1429 1430 std::vector<std::string> strs(5); 1431 sql << "select str from soci_test order by id", into(strs, inds); 1432 REQUIRE(inds.size() == 5); 1433 CHECK(inds[0] == i_ok); 1434 CHECK(inds[1] == i_ok); 1435 CHECK(inds[2] == i_null); 1436 CHECK(inds[3] == i_null); 1437 CHECK(inds[4] == i_ok); 1438 1439 strs.resize(1); 1440 sql << "select str from soci_test order by id", into(strs, inds); 1441 CHECK(inds.size() == 1); 1442 1443 strs.resize(1); 1444 st = (sql.prepare << "select str from soci_test order by id", into(strs, inds)); 1445 st.execute(); 1446 st.fetch(); 1447 CHECK(inds.size() == 1); 1448 while (st.fetch()); 1449 1450 std::vector<int> ids(1); 1451 sql << "select id from soci_test", into(ids); 1452 CHECK(ids.size() == 1); 1453 } 1454 } 1455 1456 } 1457 1458 TEST_CASE_METHOD(common_tests, "Get last insert ID", "[core][get_last_insert_id]") 1459 { 1460 soci::session sql(backEndFactory_, connectString_); 1461 1462 // create and populate the test table 1463 auto_table_creator tableCreator(tc_.table_creator_get_last_insert_id(sql)); 1464 1465 // If the get_last_insert_id() supported by the backend. 1466 if (!tableCreator.get()) 1467 return; 1468 1469 long long id; 1470 REQUIRE(sql.get_last_insert_id("soci_test", id)); 1471 // The initial value should be 1 and we call get_last_insert_id() before 1472 // the first insert, so the "pre-initial value" is 0. 1473 CHECK(id == 0); 1474 1475 sql << "insert into soci_test(val) values(10)"; 1476 1477 REQUIRE(sql.get_last_insert_id("soci_test", id)); 1478 CHECK(id == 1); 1479 1480 sql << "insert into soci_test(val) values(11)"; 1481 1482 REQUIRE(sql.get_last_insert_id("soci_test", id)); 1483 CHECK(id == 2); 1484 } 1485 1486 // "use" tests, type conversions, etc. 1487 TEST_CASE_METHOD(common_tests, "Use type conversion", "[core][use]") 1488 { 1489 soci::session sql(backEndFactory_, connectString_); 1490 1491 auto_table_creator tableCreator(tc_.table_creator_1(sql)); 1492 1493 SECTION("char") 1494 { 1495 char c('a'); 1496 sql << "insert into soci_test(c) values(:c)", use(c); 1497 1498 c = 'b'; 1499 sql << "select c from soci_test", into(c); 1500 CHECK(c == 'a'); 1501 1502 } 1503 1504 SECTION("std::string") 1505 { 1506 std::string s = "Hello SOCI!"; 1507 sql << "insert into soci_test(str) values(:s)", use(s); 1508 1509 std::string str; 1510 sql << "select str from soci_test", into(str); 1511 1512 CHECK(str == "Hello SOCI!"); 1513 } 1514 1515 SECTION("short") 1516 { 1517 short s = 123; 1518 sql << "insert into soci_test(id) values(:id)", use(s); 1519 1520 short s2 = 0; 1521 sql << "select id from soci_test", into(s2); 1522 1523 CHECK(s2 == 123); 1524 } 1525 1526 SECTION("int") 1527 { 1528 int i = -12345678; 1529 sql << "insert into soci_test(id) values(:i)", use(i); 1530 1531 int i2 = 0; 1532 sql << "select id from soci_test", into(i2); 1533 1534 CHECK(i2 == -12345678); 1535 } 1536 1537 SECTION("unsigned long") 1538 { 1539 unsigned long ul = 4000000000ul; 1540 sql << "insert into soci_test(ul) values(:num)", use(ul); 1541 1542 unsigned long ul2 = 0; 1543 sql << "select ul from soci_test", into(ul2); 1544 1545 CHECK(ul2 == 4000000000ul); 1546 } 1547 1548 SECTION("double") 1549 { 1550 double d = 3.14159265; 1551 sql << "insert into soci_test(d) values(:d)", use(d); 1552 1553 double d2 = 0; 1554 sql << "select d from soci_test", into(d2); 1555 1556 ASSERT_EQUAL(d2, d); 1557 } 1558 1559 SECTION("std::tm") 1560 { 1561 std::tm t = std::tm(); 1562 t.tm_year = 105; 1563 t.tm_mon = 10; 1564 t.tm_mday = 19; 1565 t.tm_hour = 21; 1566 t.tm_min = 39; 1567 t.tm_sec = 57; 1568 sql << "insert into soci_test(tm) values(:t)", use(t); 1569 1570 std::tm t2 = std::tm(); 1571 t2.tm_year = 0; 1572 t2.tm_mon = 0; 1573 t2.tm_mday = 0; 1574 t2.tm_hour = 0; 1575 t2.tm_min = 0; 1576 t2.tm_sec = 0; 1577 1578 sql << "select tm from soci_test", into(t2); 1579 1580 CHECK(t.tm_year == 105); 1581 CHECK(t.tm_mon == 10); 1582 CHECK(t.tm_mday == 19); 1583 CHECK(t.tm_hour == 21); 1584 CHECK(t.tm_min == 39); 1585 CHECK(t.tm_sec == 57); 1586 } 1587 1588 SECTION("repeated use") 1589 { 1590 int i; 1591 statement st = (sql.prepare 1592 << "insert into soci_test(id) values(:id)", use(i)); 1593 1594 i = 5; 1595 st.execute(true); 1596 i = 6; 1597 st.execute(true); 1598 i = 7; 1599 st.execute(true); 1600 1601 std::vector<int> v(5); 1602 sql << "select id from soci_test order by id", into(v); 1603 1604 CHECK(v.size() == 3); 1605 CHECK(v[0] == 5); 1606 CHECK(v[1] == 6); 1607 CHECK(v[2] == 7); 1608 } 1609 1610 // tests for use of const objects 1611 1612 SECTION("const char") 1613 { 1614 char const c('a'); 1615 sql << "insert into soci_test(c) values(:c)", use(c); 1616 1617 char c2 = 'b'; 1618 sql << "select c from soci_test", into(c2); 1619 CHECK(c2 == 'a'); 1620 1621 } 1622 1623 SECTION("const std::string") 1624 { 1625 std::string const s = "Hello const SOCI!"; 1626 sql << "insert into soci_test(str) values(:s)", use(s); 1627 1628 std::string str; 1629 sql << "select str from soci_test", into(str); 1630 1631 CHECK(str == "Hello const SOCI!"); 1632 } 1633 1634 SECTION("const short") 1635 { 1636 short const s = 123; 1637 sql << "insert into soci_test(id) values(:id)", use(s); 1638 1639 short s2 = 0; 1640 sql << "select id from soci_test", into(s2); 1641 1642 CHECK(s2 == 123); 1643 } 1644 1645 SECTION("const int") 1646 { 1647 int const i = -12345678; 1648 sql << "insert into soci_test(id) values(:i)", use(i); 1649 1650 int i2 = 0; 1651 sql << "select id from soci_test", into(i2); 1652 1653 CHECK(i2 == -12345678); 1654 } 1655 1656 SECTION("const unsigned long") 1657 { 1658 unsigned long const ul = 4000000000ul; 1659 sql << "insert into soci_test(ul) values(:num)", use(ul); 1660 1661 unsigned long ul2 = 0; 1662 sql << "select ul from soci_test", into(ul2); 1663 1664 CHECK(ul2 == 4000000000ul); 1665 } 1666 1667 SECTION("const double") 1668 { 1669 double const d = 3.14159265; 1670 sql << "insert into soci_test(d) values(:d)", use(d); 1671 1672 double d2 = 0; 1673 sql << "select d from soci_test", into(d2); 1674 1675 ASSERT_EQUAL(d2, d); 1676 } 1677 1678 SECTION("const std::tm") 1679 { 1680 std::tm t = std::tm(); 1681 t.tm_year = 105; 1682 t.tm_mon = 10; 1683 t.tm_mday = 19; 1684 t.tm_hour = 21; 1685 t.tm_min = 39; 1686 t.tm_sec = 57; 1687 std::tm const & ct = t; 1688 sql << "insert into soci_test(tm) values(:t)", use(ct); 1689 1690 std::tm t2 = std::tm(); 1691 t2.tm_year = 0; 1692 t2.tm_mon = 0; 1693 t2.tm_mday = 0; 1694 t2.tm_hour = 0; 1695 t2.tm_min = 0; 1696 t2.tm_sec = 0; 1697 1698 sql << "select tm from soci_test", into(t2); 1699 1700 CHECK(t.tm_year == 105); 1701 CHECK(t.tm_mon == 10); 1702 CHECK(t.tm_mday == 19); 1703 CHECK(t.tm_hour == 21); 1704 CHECK(t.tm_min == 39); 1705 CHECK(t.tm_sec == 57); 1706 } 1707 } 1708 1709 // test for multiple use (and into) elements 1710 TEST_CASE_METHOD(common_tests, "Multiple use and into", "[core][use][into]") 1711 { 1712 soci::session sql(backEndFactory_, connectString_); 1713 auto_table_creator tableCreator(tc_.table_creator_1(sql)); 1714 1715 { 1716 int i1 = 5; 1717 int i2 = 6; 1718 int i3 = 7; 1719 1720 sql << "insert into soci_test(i1, i2, i3) values(:i1, :i2, :i3)", 1721 use(i1), use(i2), use(i3); 1722 1723 i1 = 0; 1724 i2 = 0; 1725 i3 = 0; 1726 sql << "select i1, i2, i3 from soci_test", 1727 into(i1), into(i2), into(i3); 1728 1729 CHECK(i1 == 5); 1730 CHECK(i2 == 6); 1731 CHECK(i3 == 7); 1732 1733 // same for vectors 1734 sql << "delete from soci_test"; 1735 1736 i1 = 0; 1737 i2 = 0; 1738 i3 = 0; 1739 1740 statement st = (sql.prepare 1741 << "insert into soci_test(i1, i2, i3) values(:i1, :i2, :i3)", 1742 use(i1), use(i2), use(i3)); 1743 1744 i1 = 1; 1745 i2 = 2; 1746 i3 = 3; 1747 st.execute(true); 1748 i1 = 4; 1749 i2 = 5; 1750 i3 = 6; 1751 st.execute(true); 1752 i1 = 7; 1753 i2 = 8; 1754 i3 = 9; 1755 st.execute(true); 1756 1757 std::vector<int> v1(5); 1758 std::vector<int> v2(5); 1759 std::vector<int> v3(5); 1760 1761 sql << "select i1, i2, i3 from soci_test order by i1", 1762 into(v1), into(v2), into(v3); 1763 1764 CHECK(v1.size() == 3); 1765 CHECK(v2.size() == 3); 1766 CHECK(v3.size() == 3); 1767 CHECK(v1[0] == 1); 1768 CHECK(v1[1] == 4); 1769 CHECK(v1[2] == 7); 1770 CHECK(v2[0] == 2); 1771 CHECK(v2[1] == 5); 1772 CHECK(v2[2] == 8); 1773 CHECK(v3[0] == 3); 1774 CHECK(v3[1] == 6); 1775 CHECK(v3[2] == 9); 1776 } 1777 } 1778 1779 // use vector elements 1780 TEST_CASE_METHOD(common_tests, "Use vector", "[core][use][vector]") 1781 { 1782 soci::session sql(backEndFactory_, connectString_); 1783 1784 auto_table_creator tableCreator(tc_.table_creator_1(sql)); 1785 1786 SECTION("char") 1787 { 1788 std::vector<char> v; 1789 v.push_back('a'); 1790 v.push_back('b'); 1791 v.push_back('c'); 1792 v.push_back('d'); 1793 1794 sql << "insert into soci_test(c) values(:c)", use(v); 1795 1796 std::vector<char> v2(4); 1797 1798 sql << "select c from soci_test order by c", into(v2); 1799 CHECK(v2.size() == 4); 1800 CHECK(v2[0] == 'a'); 1801 CHECK(v2[1] == 'b'); 1802 CHECK(v2[2] == 'c'); 1803 CHECK(v2[3] == 'd'); 1804 } 1805 1806 SECTION("std::string") 1807 { 1808 std::vector<std::string> v; 1809 v.push_back("ala"); 1810 v.push_back("ma"); 1811 v.push_back("kota"); 1812 1813 sql << "insert into soci_test(str) values(:s)", use(v); 1814 1815 std::vector<std::string> v2(4); 1816 1817 sql << "select str from soci_test order by str", into(v2); 1818 CHECK(v2.size() == 3); 1819 CHECK(v2[0] == "ala"); 1820 CHECK(v2[1] == "kota"); 1821 CHECK(v2[2] == "ma"); 1822 } 1823 1824 SECTION("short") 1825 { 1826 std::vector<short> v; 1827 v.push_back(-5); 1828 v.push_back(6); 1829 v.push_back(7); 1830 v.push_back(123); 1831 1832 sql << "insert into soci_test(sh) values(:sh)", use(v); 1833 1834 std::vector<short> v2(4); 1835 1836 sql << "select sh from soci_test order by sh", into(v2); 1837 CHECK(v2.size() == 4); 1838 CHECK(v2[0] == -5); 1839 CHECK(v2[1] == 6); 1840 CHECK(v2[2] == 7); 1841 CHECK(v2[3] == 123); 1842 } 1843 1844 SECTION("int") 1845 { 1846 std::vector<int> v; 1847 v.push_back(-2000000000); 1848 v.push_back(0); 1849 v.push_back(1); 1850 v.push_back(2000000000); 1851 1852 sql << "insert into soci_test(id) values(:i)", use(v); 1853 1854 std::vector<int> v2(4); 1855 1856 sql << "select id from soci_test order by id", into(v2); 1857 CHECK(v2.size() == 4); 1858 CHECK(v2[0] == -2000000000); 1859 CHECK(v2[1] == 0); 1860 CHECK(v2[2] == 1); 1861 CHECK(v2[3] == 2000000000); 1862 } 1863 1864 SECTION("unsigned int") 1865 { 1866 std::vector<unsigned int> v; 1867 v.push_back(0); 1868 v.push_back(1); 1869 v.push_back(123); 1870 v.push_back(1000); 1871 1872 sql << "insert into soci_test(ul) values(:ul)", use(v); 1873 1874 std::vector<unsigned int> v2(4); 1875 1876 sql << "select ul from soci_test order by ul", into(v2); 1877 CHECK(v2.size() == 4); 1878 CHECK(v2[0] == 0); 1879 CHECK(v2[1] == 1); 1880 CHECK(v2[2] == 123); 1881 CHECK(v2[3] == 1000); 1882 } 1883 1884 SECTION("unsigned long long") 1885 { 1886 std::vector<unsigned long long> v; 1887 v.push_back(0); 1888 v.push_back(1); 1889 v.push_back(123); 1890 v.push_back(1000); 1891 1892 sql << "insert into soci_test(ul) values(:ul)", use(v); 1893 1894 std::vector<unsigned int> v2(4); 1895 1896 sql << "select ul from soci_test order by ul", into(v2); 1897 CHECK(v2.size() == 4); 1898 CHECK(v2[0] == 0); 1899 CHECK(v2[1] == 1); 1900 CHECK(v2[2] == 123); 1901 CHECK(v2[3] == 1000); 1902 } 1903 1904 SECTION("double") 1905 { 1906 std::vector<double> v; 1907 v.push_back(0); 1908 v.push_back(-0.0001); 1909 v.push_back(0.0001); 1910 v.push_back(3.1415926); 1911 1912 sql << "insert into soci_test(d) values(:d)", use(v); 1913 1914 std::vector<double> v2(4); 1915 1916 sql << "select d from soci_test order by d", into(v2); 1917 CHECK(v2.size() == 4); 1918 ASSERT_EQUAL(v2[0],-0.0001); 1919 ASSERT_EQUAL(v2[1], 0); 1920 ASSERT_EQUAL(v2[2], 0.0001); 1921 ASSERT_EQUAL(v2[3], 3.1415926); 1922 } 1923 1924 SECTION("std::tm") 1925 { 1926 std::vector<std::tm> v; 1927 std::tm t = std::tm(); 1928 t.tm_year = 105; 1929 t.tm_mon = 10; 1930 t.tm_mday = 26; 1931 t.tm_hour = 22; 1932 t.tm_min = 45; 1933 t.tm_sec = 17; 1934 1935 v.push_back(t); 1936 1937 t.tm_sec = 37; 1938 v.push_back(t); 1939 1940 t.tm_mday = 25; 1941 v.push_back(t); 1942 1943 sql << "insert into soci_test(tm) values(:t)", use(v); 1944 1945 std::vector<std::tm> v2(4); 1946 1947 sql << "select tm from soci_test order by tm", into(v2); 1948 CHECK(v2.size() == 3); 1949 CHECK(v2[0].tm_year == 105); 1950 CHECK(v2[0].tm_mon == 10); 1951 CHECK(v2[0].tm_mday == 25); 1952 CHECK(v2[0].tm_hour == 22); 1953 CHECK(v2[0].tm_min == 45); 1954 CHECK(v2[0].tm_sec == 37); 1955 CHECK(v2[1].tm_year == 105); 1956 CHECK(v2[1].tm_mon == 10); 1957 CHECK(v2[1].tm_mday == 26); 1958 CHECK(v2[1].tm_hour == 22); 1959 CHECK(v2[1].tm_min == 45); 1960 CHECK(v2[1].tm_sec == 17); 1961 CHECK(v2[2].tm_year == 105); 1962 CHECK(v2[2].tm_mon == 10); 1963 CHECK(v2[2].tm_mday == 26); 1964 CHECK(v2[2].tm_hour == 22); 1965 CHECK(v2[2].tm_min == 45); 1966 CHECK(v2[2].tm_sec == 37); 1967 } 1968 1969 SECTION("const int") 1970 { 1971 std::vector<int> v; 1972 v.push_back(-2000000000); 1973 v.push_back(0); 1974 v.push_back(1); 1975 v.push_back(2000000000); 1976 1977 std::vector<int> const & cv = v; 1978 1979 sql << "insert into soci_test(id) values(:i)", use(cv); 1980 1981 std::vector<int> v2(4); 1982 1983 sql << "select id from soci_test order by id", into(v2); 1984 CHECK(v2.size() == 4); 1985 CHECK(v2[0] == -2000000000); 1986 CHECK(v2[1] == 0); 1987 CHECK(v2[2] == 1); 1988 CHECK(v2[3] == 2000000000); 1989 } 1990 } 1991 1992 // use vector elements with type convertion 1993 TEST_CASE_METHOD(common_tests, "Use vector of custom type objects", "[core][use][vector][type_conversion]") 1994 { 1995 soci::session sql(backEndFactory_, connectString_); 1996 1997 auto_table_creator tableCreator(tc_.table_creator_1(sql)); 1998 1999 // Unfortunately there is no portable way to indicate whether nulls should 2000 // appear at the beginning or the end (SQL 2003 "NULLS LAST" is still not 2001 // supported by MS SQL in 2021...), so use this column just to order by it. 2002 std::vector<int> i; 2003 i.push_back(0); 2004 i.push_back(1); 2005 i.push_back(2); 2006 2007 std::vector<MyOptionalString> v; 2008 v.push_back(MyOptionalString("string")); // A not empty valid string. 2009 v.push_back(MyOptionalString()); // Invalid string mapped to null. 2010 v.push_back(MyOptionalString("")); // An empty but still valid string. 2011 2012 sql << "insert into soci_test(id, str) values(:i, :v)", use(i), use(v); 2013 2014 SECTION("standard type") 2015 { 2016 std::vector<std::string> values(3); 2017 std::vector<indicator> inds(3); 2018 sql << "select str from soci_test order by id", into(values, inds); 2019 2020 REQUIRE(values.size() == 3); 2021 REQUIRE(inds.size() == 3); 2022 2023 CHECK(inds[0] == soci::i_ok); 2024 CHECK(values[0] == "string"); 2025 2026 CHECK(inds[1] == soci::i_null); 2027 2028 if ( !tc_.treats_empty_strings_as_null() ) 2029 { 2030 CHECK(inds[2] == soci::i_ok); 2031 CHECK(values[2] == ""); 2032 } 2033 } 2034 2035 SECTION("user type") 2036 { 2037 std::vector<MyOptionalString> values(3); 2038 std::vector<indicator> inds(3); 2039 sql << "select str from soci_test order by id", into(values, inds); 2040 2041 REQUIRE(values.size() == 3); 2042 REQUIRE(inds.size() == 3); 2043 2044 CHECK(inds[0] == soci::i_ok); 2045 CHECK(values[0].is_valid()); 2046 CHECK(values[0].get() == "string"); 2047 2048 CHECK(!values[1].is_valid()); 2049 CHECK(inds[1] == soci::i_null); 2050 2051 if ( !tc_.treats_empty_strings_as_null() ) 2052 { 2053 CHECK(inds[2] == soci::i_ok); 2054 CHECK(values[2].is_valid()); 2055 CHECK(values[2].get() == ""); 2056 } 2057 } 2058 } 2059 2060 TEST_CASE_METHOD(common_tests, "Into vector of custom type objects", "[core][into][vector][type_conversion]") 2061 { 2062 soci::session sql(backEndFactory_, connectString_); 2063 2064 auto_table_creator tableCreator(tc_.table_creator_1(sql)); 2065 2066 // Column used for sorting only, see above. 2067 std::vector<int> i; 2068 i.push_back(0); 2069 i.push_back(1); 2070 i.push_back(2); 2071 2072 std::vector<std::string> values(3); 2073 values[0] = "string"; 2074 2075 std::vector<indicator> inds; 2076 inds.push_back(i_ok); 2077 inds.push_back(i_null); 2078 inds.push_back(i_ok); 2079 2080 sql << "insert into soci_test(id, str) values(:i, :v)", use(i), use(values, inds); 2081 2082 std::vector<MyOptionalString> v2(4); 2083 2084 sql << "select str from soci_test order by id", into(v2); 2085 2086 INFO("Got back " << v2); 2087 REQUIRE(v2.size() == 3); 2088 2089 CHECK(v2[0].is_valid()); 2090 CHECK(v2[0].get() == "string"); 2091 2092 CHECK(!v2[1].is_valid()); 2093 2094 if ( !tc_.treats_empty_strings_as_null() ) 2095 { 2096 CHECK(v2[2].is_valid()); 2097 CHECK(v2[2].get().empty()); 2098 } 2099 } 2100 2101 // test for named binding 2102 TEST_CASE_METHOD(common_tests, "Named parameters", "[core][use][named-params]") 2103 { 2104 soci::session sql(backEndFactory_, connectString_); 2105 { 2106 auto_table_creator tableCreator(tc_.table_creator_1(sql)); 2107 2108 int i1 = 7; 2109 int i2 = 8; 2110 2111 // verify the exception is thrown if both by position 2112 // and by name use elements are specified 2113 try 2114 { 2115 sql << "insert into soci_test(i1, i2) values(:i1, :i2)", 2116 use(i1, "i1"), use(i2); 2117 2118 FAIL("expected exception not thrown"); 2119 } 2120 catch (soci_error const& e) 2121 { 2122 CHECK(e.get_error_message() == 2123 "Binding for use elements must be either by position " 2124 "or by name."); 2125 } 2126 2127 // normal test 2128 sql << "insert into soci_test(i1, i2) values(:i1, :i2)", 2129 use(i1, "i1"), use(i2, "i2"); 2130 2131 i1 = 0; 2132 i2 = 0; 2133 sql << "select i1, i2 from soci_test", into(i1), into(i2); 2134 CHECK(i1 == 7); 2135 CHECK(i2 == 8); 2136 2137 i2 = 0; 2138 sql << "select i2 from soci_test where i1 = :i1", into(i2), use(i1); 2139 CHECK(i2 == 8); 2140 2141 sql << "delete from soci_test"; 2142 2143 // test vectors 2144 2145 std::vector<int> v1; 2146 v1.push_back(1); 2147 v1.push_back(2); 2148 v1.push_back(3); 2149 2150 std::vector<int> v2; 2151 v2.push_back(4); 2152 v2.push_back(5); 2153 v2.push_back(6); 2154 2155 sql << "insert into soci_test(i1, i2) values(:i1, :i2)", 2156 use(v1, "i1"), use(v2, "i2"); 2157 2158 sql << "select i2, i1 from soci_test order by i1 desc", 2159 into(v1), into(v2); 2160 CHECK(v1.size() == 3); 2161 CHECK(v2.size() == 3); 2162 CHECK(v1[0] == 6); 2163 CHECK(v1[1] == 5); 2164 CHECK(v1[2] == 4); 2165 CHECK(v2[0] == 3); 2166 CHECK(v2[1] == 2); 2167 CHECK(v2[2] == 1); 2168 } 2169 } 2170 2171 TEST_CASE_METHOD(common_tests, "Named parameters with similar names", "[core][use][named-params]") 2172 { 2173 // Verify parsing of parameters with similar names, 2174 // where one name is part of the other, etc. 2175 // https://github.com/SOCI/soci/issues/26 2176 2177 soci::session sql(backEndFactory_, connectString_); 2178 { 2179 auto_table_creator tableCreator(tc_.table_creator_1(sql)); 2180 std::string passwd("abc"); 2181 std::string passwd_clear("clear"); 2182 2183 SECTION("unnamed") 2184 { 2185 sql << "INSERT INTO soci_test(str,name) VALUES(:passwd_clear, :passwd)", 2186 soci::use(passwd), soci::use(passwd_clear); 2187 } 2188 2189 SECTION("same order") 2190 { 2191 sql << "INSERT INTO soci_test(str,name) VALUES(:passwd_clear, :passwd)", 2192 soci::use(passwd_clear, "passwd_clear"), soci::use(passwd, "passwd"); 2193 } 2194 2195 SECTION("reversed order") 2196 { 2197 sql << "INSERT INTO soci_test(str,name) VALUES(:passwd_clear, :passwd)", 2198 soci::use(passwd, "passwd"), soci::use(passwd_clear, "passwd_clear"); 2199 } 2200 2201 // TODO: Allow binding the same varibale multiple times 2202 // SECTION("one for multiple placeholders") 2203 // { 2204 // sql << "INSERT INTO soci_test(str,name) VALUES(:passwd, :passwd)", 2205 // soci::use(passwd, "passwd"); 2206 // } 2207 } 2208 } 2209 2210 // transaction test 2211 TEST_CASE_METHOD(common_tests, "Transactions", "[core][transaction]") 2212 { 2213 soci::session sql(backEndFactory_, connectString_); 2214 2215 if (!tc_.has_transactions_support(sql)) 2216 { 2217 WARN("Transactions not supported by the database, skipping the test."); 2218 return; 2219 } 2220 2221 auto_table_creator tableCreator(tc_.table_creator_1(sql)); 2222 2223 int count; 2224 sql << "select count(*) from soci_test", into(count); 2225 CHECK(count == 0); 2226 2227 { 2228 transaction tr(sql); 2229 2230 sql << "insert into soci_test (id, name) values(1, 'John')"; 2231 sql << "insert into soci_test (id, name) values(2, 'Anna')"; 2232 sql << "insert into soci_test (id, name) values(3, 'Mike')"; 2233 2234 tr.commit(); 2235 } 2236 { 2237 transaction tr(sql); 2238 2239 sql << "select count(*) from soci_test", into(count); 2240 CHECK(count == 3); 2241 2242 sql << "insert into soci_test (id, name) values(4, 'Stan')"; 2243 2244 sql << "select count(*) from soci_test", into(count); 2245 CHECK(count == 4); 2246 2247 tr.rollback(); 2248 2249 sql << "select count(*) from soci_test", into(count); 2250 CHECK(count == 3); 2251 } 2252 { 2253 transaction tr(sql); 2254 2255 sql << "delete from soci_test"; 2256 2257 sql << "select count(*) from soci_test", into(count); 2258 CHECK(count == 0); 2259 2260 tr.rollback(); 2261 2262 sql << "select count(*) from soci_test", into(count); 2263 CHECK(count == 3); 2264 } 2265 { 2266 // additional test for detection of double commit 2267 transaction tr(sql); 2268 tr.commit(); 2269 try 2270 { 2271 tr.commit(); 2272 FAIL("expected exception not thrown"); 2273 } 2274 catch (soci_error const &e) 2275 { 2276 CHECK(e.get_error_message() == 2277 "The transaction object cannot be handled twice."); 2278 } 2279 } 2280 } 2281 2282 std::tm generate_tm() 2283 { 2284 std::tm t = std::tm(); 2285 t.tm_year = 105; 2286 t.tm_mon = 10; 2287 t.tm_mday = 15; 2288 t.tm_hour = 22; 2289 t.tm_min = 14; 2290 t.tm_sec = 17; 2291 return t; 2292 } 2293 2294 // test of use elements with indicators 2295 TEST_CASE_METHOD(common_tests, "Use with indicators", "[core][use][indicator]") 2296 { 2297 soci::session sql(backEndFactory_, connectString_); 2298 2299 auto_table_creator tableCreator(tc_.table_creator_1(sql)); 2300 2301 indicator ind1 = i_ok; 2302 indicator ind2 = i_ok; 2303 indicator ind3 = i_ok; 2304 2305 int id = 1; 2306 int val = 10; 2307 std::tm tm_gen = generate_tm(); 2308 char const* insert = "insert into soci_test(id, val, tm) values(:id, :val, :tm)"; 2309 sql << insert, use(id, ind1), use(val, ind2), use(tm_gen, ind3); 2310 2311 id = 2; 2312 val = 11; 2313 ind2 = i_null; 2314 std::tm tm = std::tm(); 2315 ind3 = i_null; 2316 2317 sql << "insert into soci_test(id, val, tm) values(:id, :val, :tm)", 2318 use(id, ind1), use(val, ind2), use(tm, ind3); 2319 2320 sql << "select val from soci_test where id = 1", into(val, ind2); 2321 CHECK(ind2 == i_ok); 2322 CHECK(val == 10); 2323 sql << "select val, tm from soci_test where id = 2", into(val, ind2), into(tm, ind3); 2324 CHECK(ind2 == i_null); 2325 CHECK(ind3 == i_null); 2326 2327 std::vector<int> ids; 2328 ids.push_back(3); 2329 ids.push_back(4); 2330 ids.push_back(5); 2331 std::vector<int> vals; 2332 vals.push_back(12); 2333 vals.push_back(13); 2334 vals.push_back(14); 2335 std::vector<indicator> inds; 2336 inds.push_back(i_ok); 2337 inds.push_back(i_null); 2338 inds.push_back(i_ok); 2339 2340 sql << "insert into soci_test(id, val) values(:id, :val)", 2341 use(ids), use(vals, inds); 2342 2343 ids.resize(5); 2344 vals.resize(5); 2345 sql << "select id, val from soci_test order by id desc", 2346 into(ids), into(vals, inds); 2347 2348 CHECK(ids.size() == 5); 2349 CHECK(ids[0] == 5); 2350 CHECK(ids[1] == 4); 2351 CHECK(ids[2] == 3); 2352 CHECK(ids[3] == 2); 2353 CHECK(ids[4] == 1); 2354 CHECK(inds.size() == 5); 2355 CHECK(inds[0] == i_ok); 2356 CHECK(inds[1] == i_null); 2357 CHECK(inds[2] == i_ok); 2358 CHECK(inds[3] == i_null); 2359 CHECK(inds[4] == i_ok); 2360 CHECK(vals.size() == 5); 2361 CHECK(vals[0] == 14); 2362 CHECK(vals[2] == 12); 2363 CHECK(vals[4] == 10); 2364 } 2365 2366 // Dynamic binding to Row objects 2367 TEST_CASE_METHOD(common_tests, "Dynamic row binding", "[core][dynamic]") 2368 { 2369 soci::session sql(backEndFactory_, connectString_); 2370 2371 sql.uppercase_column_names(true); 2372 2373 auto_table_creator tableCreator(tc_.table_creator_2(sql)); 2374 2375 row r; 2376 sql << "select * from soci_test", into(r); 2377 CHECK(sql.got_data() == false); 2378 2379 sql << "insert into soci_test" 2380 " values(3.14, 123, \'Johny\'," 2381 << tc_.to_date_time("2005-12-19 22:14:17") 2382 << ", 'a')"; 2383 2384 // select into a row 2385 { 2386 statement st = (sql.prepare << 2387 "select * from soci_test", into(r)); 2388 st.execute(true); 2389 CHECK(r.size() == 5); 2390 2391 CHECK(r.get_properties(0).get_data_type() == dt_double); 2392 CHECK(r.get_properties(1).get_data_type() == dt_integer); 2393 CHECK(r.get_properties(2).get_data_type() == dt_string); 2394 CHECK(r.get_properties(3).get_data_type() == dt_date); 2395 2396 // type char is visible as string 2397 // - to comply with the implementation for Oracle 2398 CHECK(r.get_properties(4).get_data_type() == dt_string); 2399 2400 CHECK(r.get_properties("NUM_INT").get_data_type() == dt_integer); 2401 2402 CHECK(r.get_properties(0).get_name() == "NUM_FLOAT"); 2403 CHECK(r.get_properties(1).get_name() == "NUM_INT"); 2404 CHECK(r.get_properties(2).get_name() == "NAME"); 2405 CHECK(r.get_properties(3).get_name() == "SOMETIME"); 2406 CHECK(r.get_properties(4).get_name() == "CHR"); 2407 2408 ASSERT_EQUAL_APPROX(r.get<double>(0), 3.14); 2409 CHECK(r.get<int>(1) == 123); 2410 CHECK(r.get<std::string>(2) == "Johny"); 2411 CHECK(r.get<std::tm>(3).tm_year == 105); 2412 2413 // again, type char is visible as string 2414 CHECK_EQUAL_PADDED(r.get<std::string>(4), "a"); 2415 2416 ASSERT_EQUAL_APPROX(r.get<double>("NUM_FLOAT"), 3.14); 2417 CHECK(r.get<int>("NUM_INT") == 123); 2418 CHECK(r.get<std::string>("NAME") == "Johny"); 2419 CHECK_EQUAL_PADDED(r.get<std::string>("CHR"), "a"); 2420 2421 CHECK(r.get_indicator(0) == i_ok); 2422 2423 // verify exception thrown on invalid get<> 2424 bool caught = false; 2425 try 2426 { 2427 r.get<std::string>(0); 2428 } 2429 catch (std::bad_cast const &) 2430 { 2431 caught = true; 2432 } 2433 CHECK(caught); 2434 2435 // additional test for stream-like extraction 2436 { 2437 double d; 2438 int i; 2439 std::string s; 2440 std::tm t = std::tm(); 2441 std::string c; 2442 2443 r >> d >> i >> s >> t >> c; 2444 2445 ASSERT_EQUAL_APPROX(d, 3.14); 2446 CHECK(i == 123); 2447 CHECK(s == "Johny"); 2448 CHECK(t.tm_year == 105); 2449 CHECK(t.tm_mon == 11); 2450 CHECK(t.tm_mday == 19); 2451 CHECK(t.tm_hour == 22); 2452 CHECK(t.tm_min == 14); 2453 CHECK(t.tm_sec == 17); 2454 CHECK_EQUAL_PADDED(c, "a"); 2455 } 2456 } 2457 2458 // additional test to check if the row object can be 2459 // reused between queries 2460 { 2461 sql << "select * from soci_test", into(r); 2462 2463 CHECK(r.size() == 5); 2464 2465 CHECK(r.get_properties(0).get_data_type() == dt_double); 2466 CHECK(r.get_properties(1).get_data_type() == dt_integer); 2467 CHECK(r.get_properties(2).get_data_type() == dt_string); 2468 CHECK(r.get_properties(3).get_data_type() == dt_date); 2469 2470 sql << "select name, num_int from soci_test", into(r); 2471 2472 CHECK(r.size() == 2); 2473 2474 CHECK(r.get_properties(0).get_data_type() == dt_string); 2475 CHECK(r.get_properties(1).get_data_type() == dt_integer); 2476 2477 // Check if row object is movable 2478 row moved = std::move(r); 2479 2480 CHECK(moved.size() == 2); 2481 // We expect the moved-from row to become empty after the move operation 2482 CHECK(r.size() == 0); 2483 2484 CHECK(moved.get_properties(0).get_data_type() == dt_string); 2485 CHECK(moved.get_properties(1).get_data_type() == dt_integer); 2486 } 2487 } 2488 2489 // more dynamic bindings 2490 TEST_CASE_METHOD(common_tests, "Dynamic row binding 2", "[core][dynamic]") 2491 { 2492 soci::session sql(backEndFactory_, connectString_); 2493 2494 auto_table_creator tableCreator(tc_.table_creator_1(sql)); 2495 2496 sql << "insert into soci_test(id, val) values(1, 10)"; 2497 sql << "insert into soci_test(id, val) values(2, 20)"; 2498 sql << "insert into soci_test(id, val) values(3, 30)"; 2499 2500 { 2501 int id = 2; 2502 row r; 2503 sql << "select val from soci_test where id = :id", use(id), into(r); 2504 2505 CHECK(r.size() == 1); 2506 CHECK(r.get_properties(0).get_data_type() == dt_integer); 2507 CHECK(r.get<int>(0) == 20); 2508 } 2509 { 2510 int id; 2511 row r; 2512 statement st = (sql.prepare << 2513 "select val from soci_test where id = :id", use(id), into(r)); 2514 2515 id = 2; 2516 st.execute(true); 2517 CHECK(r.size() == 1); 2518 CHECK(r.get_properties(0).get_data_type() == dt_integer); 2519 CHECK(r.get<int>(0) == 20); 2520 2521 id = 3; 2522 st.execute(true); 2523 CHECK(r.size() == 1); 2524 CHECK(r.get_properties(0).get_data_type() == dt_integer); 2525 CHECK(r.get<int>(0) == 30); 2526 2527 id = 1; 2528 st.execute(true); 2529 CHECK(r.size() == 1); 2530 CHECK(r.get_properties(0).get_data_type() == dt_integer); 2531 CHECK(r.get<int>(0) == 10); 2532 } 2533 } 2534 2535 // More Dynamic binding to row objects 2536 TEST_CASE_METHOD(common_tests, "Dynamic row binding 3", "[core][dynamic]") 2537 { 2538 soci::session sql(backEndFactory_, connectString_); 2539 2540 sql.uppercase_column_names(true); 2541 2542 auto_table_creator tableCreator(tc_.table_creator_3(sql)); 2543 2544 row r1; 2545 sql << "select * from soci_test", into(r1); 2546 CHECK(sql.got_data() == false); 2547 2548 sql << "insert into soci_test values('david', '(404)123-4567')"; 2549 sql << "insert into soci_test values('john', '(404)123-4567')"; 2550 sql << "insert into soci_test values('doe', '(404)123-4567')"; 2551 2552 row r2; 2553 statement st = (sql.prepare << "select * from soci_test", into(r2)); 2554 st.execute(); 2555 2556 CHECK(r2.size() == 2); 2557 2558 int count = 0; 2559 while (st.fetch()) 2560 { 2561 ++count; 2562 CHECK(r2.get<std::string>("PHONE") == "(404)123-4567"); 2563 } 2564 CHECK(count == 3); 2565 } 2566 2567 // This is like the previous test but with a type_conversion instead of a row 2568 TEST_CASE_METHOD(common_tests, "Dynamic binding with type conversions", "[core][dynamic][type_conversion]") 2569 { 2570 soci::session sql(backEndFactory_, connectString_); 2571 2572 sql.uppercase_column_names(true); 2573 2574 SECTION("simple conversions") 2575 { 2576 auto_table_creator tableCreator(tc_.table_creator_1(sql)); 2577 2578 SECTION("between single basic type and user type") 2579 { 2580 MyInt mi; 2581 mi.set(123); 2582 sql << "insert into soci_test(id) values(:id)", use(mi); 2583 2584 int i; 2585 sql << "select id from soci_test", into(i); 2586 CHECK(i == 123); 2587 2588 sql << "update soci_test set id = id + 1"; 2589 2590 sql << "select id from soci_test", into(mi); 2591 CHECK(mi.get() == 124); 2592 } 2593 2594 SECTION("with use const") 2595 { 2596 MyInt mi; 2597 mi.set(123); 2598 2599 MyInt const & cmi = mi; 2600 sql << "insert into soci_test(id) values(:id)", use(cmi); 2601 2602 int i; 2603 sql << "select id from soci_test", into(i); 2604 CHECK(i == 123); 2605 } 2606 } 2607 2608 SECTION("ORM conversions") 2609 { 2610 auto_table_creator tableCreator(tc_.table_creator_3(sql)); 2611 2612 SECTION("conversions based on values") 2613 { 2614 PhonebookEntry p1; 2615 sql << "select * from soci_test", into(p1); 2616 CHECK(p1.name == ""); 2617 CHECK(p1.phone == ""); 2618 2619 p1.name = "david"; 2620 2621 // Note: uppercase column names are used here (and later on) 2622 // for consistency with how they can be read from database 2623 // (which means forced to uppercase on Oracle) and how they are 2624 // set/get in the type conversion routines for PhonebookEntry. 2625 // In short, IF the database is Oracle, 2626 // then all column names for binding should be uppercase. 2627 sql << "insert into soci_test values(:NAME, :PHONE)", use(p1); 2628 sql << "insert into soci_test values('john', '(404)123-4567')"; 2629 sql << "insert into soci_test values('doe', '(404)123-4567')"; 2630 2631 PhonebookEntry p2; 2632 statement st = (sql.prepare << "select * from soci_test", into(p2)); 2633 st.execute(); 2634 2635 int count = 0; 2636 while (st.fetch()) 2637 { 2638 ++count; 2639 if (p2.name == "david") 2640 { 2641 // see type_conversion<PhonebookEntry> 2642 CHECK(p2.phone =="<NULL>"); 2643 } 2644 else 2645 { 2646 CHECK(p2.phone == "(404)123-4567"); 2647 } 2648 } 2649 CHECK(count == 3); 2650 } 2651 2652 SECTION("conversions based on values with use const") 2653 { 2654 PhonebookEntry p1; 2655 p1.name = "Joe Coder"; 2656 p1.phone = "123-456"; 2657 2658 PhonebookEntry const & cp1 = p1; 2659 2660 sql << "insert into soci_test values(:NAME, :PHONE)", use(cp1); 2661 2662 PhonebookEntry p2; 2663 sql << "select * from soci_test", into(p2); 2664 CHECK(sql.got_data()); 2665 2666 CHECK(p2.name == "Joe Coder"); 2667 CHECK(p2.phone == "123-456"); 2668 } 2669 2670 SECTION("conversions based on accessor functions (as opposed to direct variable bindings)") 2671 { 2672 PhonebookEntry3 p1; 2673 p1.setName("Joe Hacker"); 2674 p1.setPhone("10010110"); 2675 2676 sql << "insert into soci_test values(:NAME, :PHONE)", use(p1); 2677 2678 PhonebookEntry3 p2; 2679 sql << "select * from soci_test", into(p2); 2680 CHECK(sql.got_data()); 2681 2682 CHECK(p2.getName() == "Joe Hacker"); 2683 CHECK(p2.getPhone() == "10010110"); 2684 } 2685 2686 SECTION("PhonebookEntry2 type conversion to test calls to values::get_indicator()") 2687 { 2688 PhonebookEntry2 p1; 2689 sql << "select * from soci_test", into(p1); 2690 CHECK(p1.name == ""); 2691 CHECK(p1.phone == ""); 2692 p1.name = "david"; 2693 2694 sql << "insert into soci_test values(:NAME, :PHONE)", use(p1); 2695 sql << "insert into soci_test values('john', '(404)123-4567')"; 2696 sql << "insert into soci_test values('doe', '(404)123-4567')"; 2697 2698 PhonebookEntry2 p2; 2699 statement st = (sql.prepare << "select * from soci_test", into(p2)); 2700 st.execute(); 2701 2702 int count = 0; 2703 while (st.fetch()) 2704 { 2705 ++count; 2706 if (p2.name == "david") 2707 { 2708 // see type_conversion<PhonebookEntry2> 2709 CHECK(p2.phone =="<NULL>"); 2710 } 2711 else 2712 { 2713 CHECK(p2.phone == "(404)123-4567"); 2714 } 2715 } 2716 CHECK(count == 3); 2717 } 2718 } 2719 } 2720 2721 TEST_CASE_METHOD(common_tests, "Prepared insert with ORM", "[core][orm]") 2722 { 2723 soci::session sql(backEndFactory_, connectString_); 2724 2725 sql.uppercase_column_names(true); 2726 auto_table_creator tableCreator(tc_.table_creator_3(sql)); 2727 2728 PhonebookEntry temp; 2729 PhonebookEntry e1 = { "name1", "phone1" }; 2730 PhonebookEntry e2 = { "name2", "phone2" }; 2731 2732 //sql << "insert into soci_test values (:NAME, :PHONE)", use(temp); 2733 statement insertStatement = (sql.prepare << "insert into soci_test values (:NAME, :PHONE)", use(temp)); 2734 2735 temp = e1; 2736 insertStatement.execute(true); 2737 temp = e2; 2738 insertStatement.execute(true); 2739 2740 int count = 0; 2741 2742 sql << "select count(*) from soci_test where NAME in ('name1', 'name2')", into(count); 2743 2744 CHECK(count == 2); 2745 } 2746 2747 TEST_CASE_METHOD(common_tests, "Partial match with ORM", "[core][orm]") 2748 { 2749 soci::session sql(backEndFactory_, connectString_); 2750 sql.uppercase_column_names(true); 2751 auto_table_creator tableCreator(tc_.table_creator_3(sql)); 2752 2753 PhonebookEntry in = { "name1", "phone1" }; 2754 std::string name = "nameA"; 2755 sql << "insert into soci_test values (:NAMED, :PHONE)", use(in), use(name, "NAMED"); 2756 2757 PhonebookEntry out; 2758 sql << "select * from soci_test where PHONE = 'phone1'", into(out); 2759 CHECK(out.name == "nameA"); 2760 CHECK(out.phone == "phone1"); 2761 } 2762 2763 TEST_CASE_METHOD(common_tests, "Numeric round trip", "[core][float]") 2764 { 2765 soci::session sql(backEndFactory_, connectString_); 2766 auto_table_creator tableCreator(tc_.table_creator_1(sql)); 2767 2768 double d1 = 0.003958, 2769 d2; 2770 2771 sql << "insert into soci_test(num76) values (:d1)", use(d1); 2772 sql << "select num76 from soci_test", into(d2); 2773 2774 // The numeric value should make the round trip unchanged, we really want 2775 // to use exact comparisons here. 2776 ASSERT_EQUAL_EXACT(d1, d2); 2777 2778 // test negative doubles too 2779 sql << "delete from soci_test"; 2780 d1 = -d1; 2781 2782 sql << "insert into soci_test(num76) values (:d1)", use(d1); 2783 sql << "select num76 from soci_test", into(d2); 2784 2785 ASSERT_EQUAL_EXACT(d1, d2); 2786 } 2787 2788 // test for bulk fetch with single use 2789 TEST_CASE_METHOD(common_tests, "Bulk fetch with single use", "[core][bulk]") 2790 { 2791 soci::session sql(backEndFactory_, connectString_); 2792 2793 auto_table_creator tableCreator(tc_.table_creator_1(sql)); 2794 2795 sql << "insert into soci_test(name, id) values('john', 1)"; 2796 sql << "insert into soci_test(name, id) values('george', 2)"; 2797 sql << "insert into soci_test(name, id) values('anthony', 1)"; 2798 sql << "insert into soci_test(name, id) values('marc', 3)"; 2799 sql << "insert into soci_test(name, id) values('julian', 1)"; 2800 2801 int code = 1; 2802 std::vector<std::string> names(10); 2803 sql << "select name from soci_test where id = :id order by name", 2804 into(names), use(code); 2805 2806 CHECK(names.size() == 3); 2807 CHECK(names[0] == "anthony"); 2808 CHECK(names[1] == "john"); 2809 CHECK(names[2] == "julian"); 2810 } 2811 2812 // test for basic logging support 2813 TEST_CASE_METHOD(common_tests, "Basic logging support", "[core][logging]") 2814 { 2815 soci::session sql(backEndFactory_, connectString_); 2816 2817 std::ostringstream log; 2818 sql.set_log_stream(&log); 2819 2820 try 2821 { 2822 sql << "drop table soci_test1"; 2823 } 2824 catch (...) {} 2825 2826 CHECK(sql.get_last_query() == "drop table soci_test1"); 2827 2828 sql.set_log_stream(NULL); 2829 2830 try 2831 { 2832 sql << "drop table soci_test2"; 2833 } 2834 catch (...) {} 2835 2836 CHECK(sql.get_last_query() == "drop table soci_test2"); 2837 2838 sql.set_log_stream(&log); 2839 2840 try 2841 { 2842 sql << "drop table soci_test3"; 2843 } 2844 catch (...) {} 2845 2846 CHECK(sql.get_last_query() == "drop table soci_test3"); 2847 CHECK(log.str() == 2848 "drop table soci_test1\n" 2849 "drop table soci_test3\n"); 2850 2851 } 2852 2853 // test for rowset creation and copying 2854 TEST_CASE_METHOD(common_tests, "Rowset creation and copying", "[core][rowset]") 2855 { 2856 soci::session sql(backEndFactory_, connectString_); 2857 2858 // create and populate the test table 2859 auto_table_creator tableCreator(tc_.table_creator_1(sql)); 2860 { 2861 // Open empty rowset 2862 rowset<row> rs1 = (sql.prepare << "select * from soci_test"); 2863 CHECK(rs1.begin() == rs1.end()); 2864 } 2865 2866 { 2867 // Copy construction 2868 rowset<row> rs1 = (sql.prepare << "select * from soci_test"); 2869 rowset<row> rs2(rs1); 2870 rowset<row> rs3(rs1); 2871 rowset<row> rs4(rs3); 2872 2873 CHECK(rs1.begin() == rs2.begin()); 2874 CHECK(rs1.begin() == rs3.begin()); 2875 CHECK(rs1.end() == rs2.end()); 2876 CHECK(rs1.end() == rs3.end()); 2877 } 2878 2879 if (!tc_.has_multiple_select_bug()) 2880 { 2881 // Assignment 2882 rowset<row> rs1 = (sql.prepare << "select * from soci_test"); 2883 rowset<row> rs2 = (sql.prepare << "select * from soci_test"); 2884 rowset<row> rs3 = (sql.prepare << "select * from soci_test"); 2885 rs1 = rs2; 2886 rs3 = rs2; 2887 2888 CHECK(rs1.begin() == rs2.begin()); 2889 CHECK(rs1.begin() == rs3.begin()); 2890 CHECK(rs1.end() == rs2.end()); 2891 CHECK(rs1.end() == rs3.end()); 2892 } 2893 } 2894 2895 // test for simple iterating using rowset iterator (without reading data) 2896 TEST_CASE_METHOD(common_tests, "Rowset iteration", "[core][rowset]") 2897 { 2898 soci::session sql(backEndFactory_, connectString_); 2899 2900 // create and populate the test table 2901 auto_table_creator tableCreator(tc_.table_creator_1(sql)); 2902 { 2903 sql << "insert into soci_test(id, val) values(1, 10)"; 2904 sql << "insert into soci_test(id, val) values(2, 11)"; 2905 sql << "insert into soci_test(id, val) values(3, NULL)"; 2906 sql << "insert into soci_test(id, val) values(4, NULL)"; 2907 sql << "insert into soci_test(id, val) values(5, 12)"; 2908 { 2909 rowset<row> rs = (sql.prepare << "select * from soci_test"); 2910 2911 CHECK(5 == std::distance(rs.begin(), rs.end())); 2912 } 2913 } 2914 2915 } 2916 2917 // test for reading rowset<row> using iterator 2918 TEST_CASE_METHOD(common_tests, "Reading rows from rowset", "[core][row][rowset]") 2919 { 2920 soci::session sql(backEndFactory_, connectString_); 2921 2922 sql.uppercase_column_names(true); 2923 2924 // create and populate the test table 2925 auto_table_creator tableCreator(tc_.table_creator_2(sql)); 2926 { 2927 { 2928 // Empty rowset 2929 rowset<row> rs = (sql.prepare << "select * from soci_test"); 2930 CHECK(0 == std::distance(rs.begin(), rs.end())); 2931 } 2932 2933 { 2934 // Non-empty rowset 2935 sql << "insert into soci_test values(3.14, 123, \'Johny\'," 2936 << tc_.to_date_time("2005-12-19 22:14:17") 2937 << ", 'a')"; 2938 sql << "insert into soci_test values(6.28, 246, \'Robert\'," 2939 << tc_.to_date_time("2004-10-01 18:44:10") 2940 << ", 'b')"; 2941 2942 rowset<row> rs = (sql.prepare << "select * from soci_test"); 2943 2944 rowset<row>::const_iterator it = rs.begin(); 2945 CHECK(it != rs.end()); 2946 2947 // 2948 // First row 2949 // 2950 row const & r1 = (*it); 2951 2952 // Properties 2953 CHECK(r1.size() == 5); 2954 CHECK(r1.get_properties(0).get_data_type() == dt_double); 2955 CHECK(r1.get_properties(1).get_data_type() == dt_integer); 2956 CHECK(r1.get_properties(2).get_data_type() == dt_string); 2957 CHECK(r1.get_properties(3).get_data_type() == dt_date); 2958 CHECK(r1.get_properties(4).get_data_type() == dt_string); 2959 CHECK(r1.get_properties("NUM_INT").get_data_type() == dt_integer); 2960 2961 // Data 2962 2963 // Since we didn't specify order by in the above query, 2964 // the 2 rows may be returned in either order 2965 // (If we specify order by, we can't do it in a cross db 2966 // compatible way, because the Oracle table for this has been 2967 // created with lower case column names) 2968 2969 std::string name = r1.get<std::string>(2); 2970 2971 if (name == "Johny") 2972 { 2973 ASSERT_EQUAL_APPROX(r1.get<double>(0), 3.14); 2974 CHECK(r1.get<int>(1) == 123); 2975 CHECK(r1.get<std::string>(2) == "Johny"); 2976 std::tm t1 = std::tm(); 2977 t1 = r1.get<std::tm>(3); 2978 CHECK(t1.tm_year == 105); 2979 CHECK_EQUAL_PADDED(r1.get<std::string>(4), "a"); 2980 ASSERT_EQUAL_APPROX(r1.get<double>("NUM_FLOAT"), 3.14); 2981 CHECK(r1.get<int>("NUM_INT") == 123); 2982 CHECK(r1.get<std::string>("NAME") == "Johny"); 2983 CHECK_EQUAL_PADDED(r1.get<std::string>("CHR"), "a"); 2984 } 2985 else if (name == "Robert") 2986 { 2987 ASSERT_EQUAL(r1.get<double>(0), 6.28); 2988 CHECK(r1.get<int>(1) == 246); 2989 CHECK(r1.get<std::string>(2) == "Robert"); 2990 std::tm t1 = r1.get<std::tm>(3); 2991 CHECK(t1.tm_year == 104); 2992 CHECK(r1.get<std::string>(4) == "b"); 2993 ASSERT_EQUAL(r1.get<double>("NUM_FLOAT"), 6.28); 2994 CHECK(r1.get<int>("NUM_INT") == 246); 2995 CHECK(r1.get<std::string>("NAME") == "Robert"); 2996 CHECK_EQUAL_PADDED(r1.get<std::string>("CHR"), "b"); 2997 } 2998 else 2999 { 3000 CAPTURE(name); 3001 FAIL("expected \"Johny\" or \"Robert\""); 3002 } 3003 3004 // 3005 // Iterate to second row 3006 // 3007 ++it; 3008 CHECK(it != rs.end()); 3009 3010 // 3011 // Second row 3012 // 3013 row const & r2 = (*it); 3014 3015 // Properties 3016 CHECK(r2.size() == 5); 3017 CHECK(r2.get_properties(0).get_data_type() == dt_double); 3018 CHECK(r2.get_properties(1).get_data_type() == dt_integer); 3019 CHECK(r2.get_properties(2).get_data_type() == dt_string); 3020 CHECK(r2.get_properties(3).get_data_type() == dt_date); 3021 CHECK(r2.get_properties(4).get_data_type() == dt_string); 3022 CHECK(r2.get_properties("NUM_INT").get_data_type() == dt_integer); 3023 3024 std::string newName = r2.get<std::string>(2); 3025 CHECK(name != newName); 3026 3027 if (newName == "Johny") 3028 { 3029 ASSERT_EQUAL_APPROX(r2.get<double>(0), 3.14); 3030 CHECK(r2.get<int>(1) == 123); 3031 CHECK(r2.get<std::string>(2) == "Johny"); 3032 std::tm t2 = r2.get<std::tm>(3); 3033 CHECK(t2.tm_year == 105); 3034 CHECK(r2.get<std::string>(4) == "a"); 3035 ASSERT_EQUAL_APPROX(r2.get<double>("NUM_FLOAT"), 3.14); 3036 CHECK(r2.get<int>("NUM_INT") == 123); 3037 CHECK(r2.get<std::string>("NAME") == "Johny"); 3038 CHECK(r2.get<std::string>("CHR") == "a"); 3039 } 3040 else if (newName == "Robert") 3041 { 3042 ASSERT_EQUAL_APPROX(r2.get<double>(0), 6.28); 3043 CHECK(r2.get<int>(1) == 246); 3044 CHECK(r2.get<std::string>(2) == "Robert"); 3045 std::tm t2 = r2.get<std::tm>(3); 3046 CHECK(t2.tm_year == 104); 3047 CHECK_EQUAL_PADDED(r2.get<std::string>(4), "b"); 3048 ASSERT_EQUAL_APPROX(r2.get<double>("NUM_FLOAT"), 6.28); 3049 CHECK(r2.get<int>("NUM_INT") == 246); 3050 CHECK(r2.get<std::string>("NAME") == "Robert"); 3051 CHECK_EQUAL_PADDED(r2.get<std::string>("CHR"), "b"); 3052 } 3053 else 3054 { 3055 CAPTURE(newName); 3056 FAIL("expected \"Johny\" or \"Robert\""); 3057 } 3058 } 3059 3060 { 3061 // Non-empty rowset with NULL values 3062 sql << "insert into soci_test " 3063 << "(num_int, num_float , name, sometime, chr) " 3064 << "values (0, NULL, NULL, NULL, NULL)"; 3065 3066 rowset<row> rs = (sql.prepare 3067 << "select num_int, num_float, name, sometime, chr " 3068 << "from soci_test where num_int = 0"); 3069 3070 rowset<row>::const_iterator it = rs.begin(); 3071 CHECK(it != rs.end()); 3072 3073 // 3074 // First row 3075 // 3076 row const& r1 = (*it); 3077 3078 // Properties 3079 CHECK(r1.size() == 5); 3080 CHECK(r1.get_properties(0).get_data_type() == dt_integer); 3081 CHECK(r1.get_properties(1).get_data_type() == dt_double); 3082 CHECK(r1.get_properties(2).get_data_type() == dt_string); 3083 CHECK(r1.get_properties(3).get_data_type() == dt_date); 3084 CHECK(r1.get_properties(4).get_data_type() == dt_string); 3085 3086 // Data 3087 CHECK(r1.get_indicator(0) == soci::i_ok); 3088 CHECK(r1.get<int>(0) == 0); 3089 CHECK(r1.get_indicator(1) == soci::i_null); 3090 CHECK(r1.get_indicator(2) == soci::i_null); 3091 CHECK(r1.get_indicator(3) == soci::i_null); 3092 CHECK(r1.get_indicator(4) == soci::i_null); 3093 } 3094 } 3095 } 3096 3097 // test for reading rowset<int> using iterator 3098 TEST_CASE_METHOD(common_tests, "Reading ints from rowset", "[core][rowset]") 3099 { 3100 soci::session sql(backEndFactory_, connectString_); 3101 3102 // create and populate the test table 3103 auto_table_creator tableCreator(tc_.table_creator_1(sql)); 3104 { 3105 sql << "insert into soci_test(id) values(1)"; 3106 sql << "insert into soci_test(id) values(2)"; 3107 sql << "insert into soci_test(id) values(3)"; 3108 sql << "insert into soci_test(id) values(4)"; 3109 sql << "insert into soci_test(id) values(5)"; 3110 { 3111 rowset<int> rs = (sql.prepare << "select id from soci_test order by id asc"); 3112 3113 // 1st row 3114 rowset<int>::const_iterator pos = rs.begin(); 3115 CHECK(1 == (*pos)); 3116 3117 // 3rd row 3118 std::advance(pos, 2); 3119 CHECK(3 == (*pos)); 3120 3121 // 5th row 3122 std::advance(pos, 2); 3123 CHECK(5 == (*pos)); 3124 3125 // The End 3126 ++pos; 3127 CHECK(pos == rs.end()); 3128 } 3129 } 3130 3131 } 3132 3133 // test for handling 'use' and reading rowset<std::string> using iterator 3134 TEST_CASE_METHOD(common_tests, "Reading strings from rowset", "[core][rowset]") 3135 { 3136 soci::session sql(backEndFactory_, connectString_); 3137 3138 // create and populate the test table 3139 auto_table_creator tableCreator(tc_.table_creator_1(sql)); 3140 { 3141 sql << "insert into soci_test(str) values('abc')"; 3142 sql << "insert into soci_test(str) values('def')"; 3143 sql << "insert into soci_test(str) values('ghi')"; 3144 sql << "insert into soci_test(str) values('jkl')"; 3145 { 3146 // Expected result in numbers 3147 std::string idle("def"); 3148 rowset<std::string> rs1 = (sql.prepare 3149 << "select str from soci_test where str = :idle", 3150 use(idle)); 3151 3152 CHECK(1 == std::distance(rs1.begin(), rs1.end())); 3153 3154 // Expected result in value 3155 idle = "jkl"; 3156 rowset<std::string> rs2 = (sql.prepare 3157 << "select str from soci_test where str = :idle", 3158 use(idle)); 3159 3160 CHECK(idle == *(rs2.begin())); 3161 } 3162 } 3163 3164 } 3165 3166 // test for handling troublemaker 3167 TEST_CASE_METHOD(common_tests, "Rowset expected exception", "[core][exception][rowset]") 3168 { 3169 soci::session sql(backEndFactory_, connectString_); 3170 3171 // create and populate the test table 3172 auto_table_creator tableCreator(tc_.table_creator_1(sql)); 3173 sql << "insert into soci_test(str) values('abc')"; 3174 3175 std::string troublemaker; 3176 CHECK_THROWS_AS( 3177 rowset<std::string>((sql.prepare << "select str from soci_test", into(troublemaker))), 3178 soci_error 3179 ); 3180 } 3181 3182 // functor for next test 3183 struct THelper 3184 { 3185 THelper() 3186 : val_() 3187 { 3188 } 3189 void operator()(int i) 3190 { 3191 val_ = i; 3192 } 3193 int val_; 3194 }; 3195 3196 // test for handling NULL values with expected exception: 3197 // "Null value fetched and no indicator defined." 3198 TEST_CASE_METHOD(common_tests, "NULL expected exception", "[core][exception][null]") 3199 { 3200 soci::session sql(backEndFactory_, connectString_); 3201 3202 // create and populate the test table 3203 auto_table_creator tableCreator(tc_.table_creator_1(sql)); 3204 sql << "insert into soci_test(val) values(1)"; 3205 sql << "insert into soci_test(val) values(2)"; 3206 sql << "insert into soci_test(val) values(NULL)"; 3207 sql << "insert into soci_test(val) values(3)"; 3208 3209 rowset<int> rs = (sql.prepare << "select val from soci_test order by val asc"); 3210 3211 CHECK_THROWS_AS( std::for_each(rs.begin(), rs.end(), THelper()), soci_error ); 3212 } 3213 3214 TEST_CASE_METHOD(common_tests, "soci_error is nothrow", "[core][exception][nothrow]") 3215 { 3216 CHECK(std::is_nothrow_copy_assignable<soci_error>::value == true); 3217 CHECK(std::is_nothrow_copy_constructible<soci_error>::value == true); 3218 CHECK(std::is_nothrow_destructible<soci_error>::value == true); 3219 } 3220 3221 // This is like the first dynamic binding test but with rowset and iterators use 3222 TEST_CASE_METHOD(common_tests, "Dynamic binding with rowset", "[core][dynamic][type_conversion]") 3223 { 3224 soci::session sql(backEndFactory_, connectString_); 3225 3226 sql.uppercase_column_names(true); 3227 3228 { 3229 auto_table_creator tableCreator(tc_.table_creator_3(sql)); 3230 3231 PhonebookEntry p1; 3232 sql << "select * from soci_test", into(p1); 3233 CHECK(p1.name == ""); 3234 CHECK(p1.phone == ""); 3235 3236 p1.name = "david"; 3237 3238 sql << "insert into soci_test values(:NAME, :PHONE)", use(p1); 3239 sql << "insert into soci_test values('john', '(404)123-4567')"; 3240 sql << "insert into soci_test values('doe', '(404)123-4567')"; 3241 3242 rowset<PhonebookEntry> rs = (sql.prepare << "select * from soci_test"); 3243 3244 int count = 0; 3245 for (rowset<PhonebookEntry>::const_iterator it = rs.begin(); it != rs.end(); ++it) 3246 { 3247 ++count; 3248 PhonebookEntry const& p2 = (*it); 3249 if (p2.name == "david") 3250 { 3251 // see type_conversion<PhonebookEntry> 3252 CHECK(p2.phone =="<NULL>"); 3253 } 3254 else 3255 { 3256 CHECK(p2.phone == "(404)123-4567"); 3257 } 3258 } 3259 3260 CHECK(3 == count); 3261 } 3262 } 3263 3264 #ifdef SOCI_HAVE_BOOST 3265 3266 // test for handling NULL values with boost::optional 3267 // (both into and use) 3268 TEST_CASE_METHOD(common_tests, "NULL with optional", "[core][boost][null]") 3269 { 3270 3271 soci::session sql(backEndFactory_, connectString_); 3272 3273 // create and populate the test table 3274 auto_table_creator tableCreator0(tc_.table_creator_1(sql)); 3275 { 3276 sql << "insert into soci_test(val) values(7)"; 3277 3278 { 3279 // verify non-null value is fetched correctly 3280 boost::optional<int> opt; 3281 sql << "select val from soci_test", into(opt); 3282 CHECK(opt.is_initialized()); 3283 CHECK(opt.get() == 7); 3284 3285 // indicators can be used with optional 3286 // (although that's just a consequence of implementation, 3287 // not an intended feature - but let's test it anyway) 3288 indicator ind; 3289 opt.reset(); 3290 sql << "select val from soci_test", into(opt, ind); 3291 CHECK(opt.is_initialized()); 3292 CHECK(opt.get() == 7); 3293 CHECK(ind == i_ok); 3294 3295 // verify null value is fetched correctly 3296 sql << "select i1 from soci_test", into(opt); 3297 CHECK(opt.is_initialized() == false); 3298 3299 // and with indicator 3300 opt = 5; 3301 sql << "select i1 from soci_test", into(opt, ind); 3302 CHECK(opt.is_initialized() == false); 3303 CHECK(ind == i_null); 3304 3305 // verify non-null is inserted correctly 3306 opt = 3; 3307 sql << "update soci_test set val = :v", use(opt); 3308 int j = 0; 3309 sql << "select val from soci_test", into(j); 3310 CHECK(j == 3); 3311 3312 // verify null is inserted correctly 3313 opt.reset(); 3314 sql << "update soci_test set val = :v", use(opt); 3315 ind = i_ok; 3316 sql << "select val from soci_test", into(j, ind); 3317 CHECK(ind == i_null); 3318 } 3319 3320 // vector tests (select) 3321 3322 { 3323 sql << "delete from soci_test"; 3324 3325 // simple readout of non-null data 3326 3327 sql << "insert into soci_test(id, val, str) values(1, 5, \'abc\')"; 3328 sql << "insert into soci_test(id, val, str) values(2, 6, \'def\')"; 3329 sql << "insert into soci_test(id, val, str) values(3, 7, \'ghi\')"; 3330 sql << "insert into soci_test(id, val, str) values(4, 8, null)"; 3331 sql << "insert into soci_test(id, val, str) values(5, 9, \'mno\')"; 3332 3333 std::vector<boost::optional<int> > v(10); 3334 sql << "select val from soci_test order by val", into(v); 3335 3336 CHECK(v.size() == 5); 3337 CHECK(v[0].is_initialized()); 3338 CHECK(v[0].get() == 5); 3339 CHECK(v[1].is_initialized()); 3340 CHECK(v[1].get() == 6); 3341 CHECK(v[2].is_initialized()); 3342 CHECK(v[2].get() == 7); 3343 CHECK(v[3].is_initialized()); 3344 CHECK(v[3].get() == 8); 3345 CHECK(v[4].is_initialized()); 3346 CHECK(v[4].get() == 9); 3347 3348 // readout of nulls 3349 3350 sql << "update soci_test set val = null where id = 2 or id = 4"; 3351 3352 std::vector<int> ids(5); 3353 sql << "select id, val from soci_test order by id", into(ids), into(v); 3354 3355 CHECK(v.size() == 5); 3356 CHECK(ids.size() == 5); 3357 CHECK(v[0].is_initialized()); 3358 CHECK(v[0].get() == 5); 3359 CHECK(v[1].is_initialized() == false); 3360 CHECK(v[2].is_initialized()); 3361 CHECK(v[2].get() == 7); 3362 CHECK(v[3].is_initialized() == false); 3363 CHECK(v[4].is_initialized()); 3364 CHECK(v[4].get() == 9); 3365 3366 // readout with statement preparation 3367 3368 int id = 1; 3369 3370 ids.resize(3); 3371 v.resize(3); 3372 statement st = (sql.prepare << 3373 "select id, val from soci_test order by id", into(ids), into(v)); 3374 st.execute(); 3375 while (st.fetch()) 3376 { 3377 for (std::size_t i = 0; i != v.size(); ++i) 3378 { 3379 CHECK(id == ids[i]); 3380 3381 if (id == 2 || id == 4) 3382 { 3383 CHECK(v[i].is_initialized() == false); 3384 } 3385 else 3386 { 3387 CHECK(v[i].is_initialized()); 3388 CHECK(v[i].get() == id + 4); 3389 } 3390 3391 ++id; 3392 } 3393 3394 ids.resize(3); 3395 v.resize(3); 3396 } 3397 CHECK(id == 6); 3398 } 3399 3400 // and why not stress iterators and the dynamic binding, too! 3401 3402 { 3403 rowset<row> rs = (sql.prepare << "select id, val, str from soci_test order by id"); 3404 3405 rowset<row>::const_iterator it = rs.begin(); 3406 CHECK(it != rs.end()); 3407 3408 row const& r1 = (*it); 3409 3410 CHECK(r1.size() == 3); 3411 3412 // Note: for the reason of differences between number(x,y) type and 3413 // binary representation of integers, the following commented assertions 3414 // do not work for Oracle. 3415 // The problem is that for this single table the data type used in Oracle 3416 // table creator for the id column is number(10,0), 3417 // which allows to insert all int values. 3418 // On the other hand, the column description scheme used in the Oracle 3419 // backend figures out that the natural type for such a column 3420 // is eUnsignedInt - this makes the following assertions fail. 3421 // Other database backends (like PostgreSQL) use other types like int 3422 // and this not only allows to insert all int values (obviously), 3423 // but is also recognized as int (obviously). 3424 // There is a similar problem with stream-like extraction, 3425 // where internally get<T> is called and the type mismatch is detected 3426 // for the id column - that's why the code below skips this column 3427 // and tests the remaining column only. 3428 3429 //CHECK(r1.get_properties(0).get_data_type() == dt_integer); 3430 CHECK(r1.get_properties(1).get_data_type() == dt_integer); 3431 CHECK(r1.get_properties(2).get_data_type() == dt_string); 3432 //CHECK(r1.get<int>(0) == 1); 3433 CHECK(r1.get<int>(1) == 5); 3434 CHECK(r1.get<std::string>(2) == "abc"); 3435 CHECK(r1.get<boost::optional<int> >(1).is_initialized()); 3436 CHECK(r1.get<boost::optional<int> >(1).get() == 5); 3437 CHECK(r1.get<boost::optional<std::string> >(2).is_initialized()); 3438 CHECK(r1.get<boost::optional<std::string> >(2).get() == "abc"); 3439 3440 ++it; 3441 3442 row const& r2 = (*it); 3443 3444 CHECK(r2.size() == 3); 3445 3446 // CHECK(r2.get_properties(0).get_data_type() == dt_integer); 3447 CHECK(r2.get_properties(1).get_data_type() == dt_integer); 3448 CHECK(r2.get_properties(2).get_data_type() == dt_string); 3449 //CHECK(r2.get<int>(0) == 2); 3450 try 3451 { 3452 // expect exception here, this is NULL value 3453 (void)r1.get<int>(1); 3454 FAIL("expected exception not thrown"); 3455 } 3456 catch (soci_error const &) {} 3457 3458 // but we can read it as optional 3459 CHECK(r2.get<boost::optional<int> >(1).is_initialized() == false); 3460 3461 // stream-like data extraction 3462 3463 ++it; 3464 row const &r3 = (*it); 3465 3466 boost::optional<int> io; 3467 boost::optional<std::string> so; 3468 3469 r3.skip(); // move to val and str columns 3470 r3 >> io >> so; 3471 3472 CHECK(io.is_initialized()); 3473 CHECK(io.get() == 7); 3474 CHECK(so.is_initialized()); 3475 CHECK(so.get() == "ghi"); 3476 3477 ++it; 3478 row const &r4 = (*it); 3479 3480 r3.skip(); // move to val and str columns 3481 r4 >> io >> so; 3482 3483 CHECK(io.is_initialized() == false); 3484 CHECK(so.is_initialized() == false); 3485 } 3486 3487 // inserts of non-null const data 3488 { 3489 sql << "delete from soci_test"; 3490 3491 const int id = 10; 3492 const boost::optional<int> val = 11; 3493 3494 sql << "insert into soci_test(id, val) values(:id, :val)", 3495 use(id, "id"), 3496 use(val, "val"); 3497 3498 int sum; 3499 sql << "select sum(val) from soci_test", into(sum); 3500 CHECK(sum == 11); 3501 } 3502 3503 // bulk inserts of non-null data 3504 3505 { 3506 sql << "delete from soci_test"; 3507 3508 std::vector<int> ids; 3509 std::vector<boost::optional<int> > v; 3510 3511 ids.push_back(10); v.push_back(20); 3512 ids.push_back(11); v.push_back(21); 3513 ids.push_back(12); v.push_back(22); 3514 ids.push_back(13); v.push_back(23); 3515 3516 sql << "insert into soci_test(id, val) values(:id, :val)", 3517 use(ids, "id"), use(v, "val"); 3518 3519 int sum; 3520 sql << "select sum(val) from soci_test", into(sum); 3521 CHECK(sum == 86); 3522 3523 // bulk inserts of some-null data 3524 3525 sql << "delete from soci_test"; 3526 3527 v[2].reset(); 3528 v[3].reset(); 3529 3530 sql << "insert into soci_test(id, val) values(:id, :val)", 3531 use(ids, "id"), use(v, "val"); 3532 3533 sql << "select sum(val) from soci_test", into(sum); 3534 CHECK(sum == 41); 3535 } 3536 3537 3538 // bulk inserts of non-null data with const vector 3539 3540 { 3541 sql << "delete from soci_test"; 3542 3543 std::vector<int> ids; 3544 std::vector<boost::optional<int> > v; 3545 3546 ids.push_back(10); v.push_back(20); 3547 ids.push_back(11); v.push_back(21); 3548 ids.push_back(12); v.push_back(22); 3549 ids.push_back(13); v.push_back(23); 3550 3551 const std::vector<int>& cref_ids = ids; 3552 const std::vector<boost::optional<int> >& cref_v = v; 3553 3554 sql << "insert into soci_test(id, val) values(:id, :val)", 3555 use(cref_ids, "id"), 3556 use(cref_v, "val"); 3557 3558 int sum; 3559 sql << "select sum(val) from soci_test", into(sum); 3560 CHECK(sum == 86); 3561 3562 // bulk inserts of some-null data 3563 3564 sql << "delete from soci_test"; 3565 3566 v[2].reset(); 3567 v[3].reset(); 3568 3569 sql << "insert into soci_test(id, val) values(:id, :val)", 3570 use(cref_ids, "id"), 3571 use(cref_v, "val"); 3572 3573 sql << "select sum(val) from soci_test", into(sum); 3574 CHECK(sum == 41); 3575 } 3576 3577 // composability with user conversions 3578 3579 { 3580 sql << "delete from soci_test"; 3581 3582 boost::optional<MyInt> omi1; 3583 boost::optional<MyInt> omi2; 3584 3585 omi1 = MyInt(125); 3586 omi2.reset(); 3587 3588 sql << "insert into soci_test(id, val) values(:id, :val)", 3589 use(omi1), use(omi2); 3590 3591 sql << "select id, val from soci_test", into(omi2), into(omi1); 3592 3593 CHECK(omi1.is_initialized() == false); 3594 CHECK(omi2.is_initialized()); 3595 CHECK(omi2.get().get() == 125); 3596 } 3597 3598 // use with const optional and user conversions 3599 3600 { 3601 sql << "delete from soci_test"; 3602 3603 boost::optional<MyInt> omi1; 3604 boost::optional<MyInt> omi2; 3605 3606 omi1 = MyInt(125); 3607 omi2.reset(); 3608 3609 boost::optional<MyInt> const & comi1 = omi1; 3610 boost::optional<MyInt> const & comi2 = omi2; 3611 3612 sql << "insert into soci_test(id, val) values(:id, :val)", 3613 use(comi1), use(comi2); 3614 3615 sql << "select id, val from soci_test", into(omi2), into(omi1); 3616 3617 CHECK(omi1.is_initialized() == false); 3618 CHECK(omi2.is_initialized()); 3619 CHECK(omi2.get().get() == 125); 3620 } 3621 3622 // use with rowset and table containing null values 3623 3624 { 3625 auto_table_creator tableCreator(tc_.table_creator_1(sql)); 3626 3627 sql << "insert into soci_test(id, val) values(1, 10)"; 3628 sql << "insert into soci_test(id, val) values(2, 11)"; 3629 sql << "insert into soci_test(id, val) values(3, NULL)"; 3630 sql << "insert into soci_test(id, val) values(4, 13)"; 3631 3632 rowset<boost::optional<int> > rs = (sql.prepare << 3633 "select val from soci_test order by id asc"); 3634 3635 // 1st row 3636 rowset<boost::optional<int> >::const_iterator pos = rs.begin(); 3637 CHECK((*pos).is_initialized()); 3638 CHECK(10 == (*pos).get()); 3639 3640 // 2nd row 3641 ++pos; 3642 CHECK((*pos).is_initialized()); 3643 CHECK(11 == (*pos).get()); 3644 3645 // 3rd row 3646 ++pos; 3647 CHECK((*pos).is_initialized() == false); 3648 3649 // 4th row 3650 ++pos; 3651 CHECK((*pos).is_initialized()); 3652 CHECK(13 == (*pos).get()); 3653 } 3654 3655 // inserting using an i_null indicator with a boost::optional should 3656 // insert null, even if the optional is valid, just as with standard 3657 // types 3658 { 3659 auto_table_creator tableCreator(tc_.table_creator_1(sql)); 3660 3661 { 3662 indicator ind = i_null; 3663 boost::optional<int> v1(10); 3664 sql << "insert into soci_test(id, val) values(1, :val)", 3665 use(v1, ind); 3666 } 3667 3668 // verify the value is fetched correctly as null 3669 { 3670 indicator ind; 3671 boost::optional<int> opt; 3672 3673 ind = i_truncated; 3674 opt = 0; 3675 sql << "select val from soci_test where id = 1", into(opt, ind); 3676 CHECK(ind == i_null); 3677 CHECK(!opt.is_initialized()); 3678 } 3679 } 3680 3681 // prepared statement inserting non-null and null values alternatively 3682 // (without passing an explicit indicator) 3683 { 3684 auto_table_creator tableCreator(tc_.table_creator_1(sql)); 3685 3686 { 3687 int id; 3688 boost::optional<int> val; 3689 statement st = (sql.prepare 3690 << "insert into soci_test(id, val) values (:id, :val)", 3691 use(id), use(val)); 3692 3693 id = 1; 3694 val = 10; 3695 st.execute(true); 3696 3697 id = 2; 3698 val = boost::optional<int>(); 3699 st.execute(true); 3700 3701 id = 3; 3702 val = 11; 3703 st.execute(true); 3704 } 3705 3706 // verify values are fetched correctly 3707 { 3708 indicator ind; 3709 boost::optional<int> opt; 3710 3711 ind = i_truncated; 3712 opt = 0; 3713 sql << "select val from soci_test where id = 1", into(opt, ind); 3714 CHECK(ind == i_ok); 3715 CHECK(opt.is_initialized()); 3716 CHECK(opt.get() == 10); 3717 3718 ind = i_truncated; 3719 opt = 0; 3720 sql << "select val from soci_test where id = 2", into(opt, ind); 3721 CHECK(ind == i_null); 3722 CHECK(!opt.is_initialized()); 3723 3724 ind = i_truncated; 3725 opt = 0; 3726 sql << "select val from soci_test where id = 3", into(opt, ind); 3727 CHECK(ind == i_ok); 3728 REQUIRE(opt.is_initialized()); 3729 CHECK(opt.get() == 11); 3730 } 3731 } 3732 } 3733 } 3734 3735 #endif // SOCI_HAVE_BOOST 3736 3737 #ifdef SOCI_HAVE_CXX17 3738 3739 // test for handling NULL values with std::optional 3740 // (both into and use) 3741 TEST_CASE_METHOD(common_tests, "NULL with std optional", "[core][null]") 3742 { 3743 3744 soci::session sql(backEndFactory_, connectString_); 3745 3746 // create and populate the test table 3747 auto_table_creator tableCreator0(tc_.table_creator_1(sql)); 3748 { 3749 sql << "insert into soci_test(val) values(7)"; 3750 3751 { 3752 // verify non-null value is fetched correctly 3753 std::optional<int> opt; 3754 sql << "select val from soci_test", into(opt); 3755 CHECK(opt.has_value()); 3756 CHECK(opt.value() == 7); 3757 3758 // indicators can be used with optional 3759 // (although that's just a consequence of implementation, 3760 // not an intended feature - but let's test it anyway) 3761 indicator ind; 3762 opt.reset(); 3763 sql << "select val from soci_test", into(opt, ind); 3764 CHECK(opt.has_value()); 3765 CHECK(opt.value() == 7); 3766 CHECK(ind == i_ok); 3767 3768 // verify null value is fetched correctly 3769 sql << "select i1 from soci_test", into(opt); 3770 CHECK(opt.has_value() == false); 3771 3772 // and with indicator 3773 opt = 5; 3774 sql << "select i1 from soci_test", into(opt, ind); 3775 CHECK(opt.has_value() == false); 3776 CHECK(ind == i_null); 3777 3778 // verify non-null is inserted correctly 3779 opt = 3; 3780 sql << "update soci_test set val = :v", use(opt); 3781 int j = 0; 3782 sql << "select val from soci_test", into(j); 3783 CHECK(j == 3); 3784 3785 // verify null is inserted correctly 3786 opt.reset(); 3787 sql << "update soci_test set val = :v", use(opt); 3788 ind = i_ok; 3789 sql << "select val from soci_test", into(j, ind); 3790 CHECK(ind == i_null); 3791 } 3792 3793 // vector tests (select) 3794 3795 { 3796 sql << "delete from soci_test"; 3797 3798 // simple readout of non-null data 3799 3800 sql << "insert into soci_test(id, val, str) values(1, 5, \'abc\')"; 3801 sql << "insert into soci_test(id, val, str) values(2, 6, \'def\')"; 3802 sql << "insert into soci_test(id, val, str) values(3, 7, \'ghi\')"; 3803 sql << "insert into soci_test(id, val, str) values(4, 8, null)"; 3804 sql << "insert into soci_test(id, val, str) values(5, 9, \'mno\')"; 3805 3806 std::vector<std::optional<int> > v(10); 3807 sql << "select val from soci_test order by val", into(v); 3808 3809 CHECK(v.size() == 5); 3810 CHECK(v[0].has_value()); 3811 CHECK(v[0].value() == 5); 3812 CHECK(v[1].has_value()); 3813 CHECK(v[1].value() == 6); 3814 CHECK(v[2].has_value()); 3815 CHECK(v[2].value() == 7); 3816 CHECK(v[3].has_value()); 3817 CHECK(v[3].value() == 8); 3818 CHECK(v[4].has_value()); 3819 CHECK(v[4].value() == 9); 3820 3821 // readout of nulls 3822 3823 sql << "update soci_test set val = null where id = 2 or id = 4"; 3824 3825 std::vector<int> ids(5); 3826 sql << "select id, val from soci_test order by id", into(ids), into(v); 3827 3828 CHECK(v.size() == 5); 3829 CHECK(ids.size() == 5); 3830 CHECK(v[0].has_value()); 3831 CHECK(v[0].value() == 5); 3832 CHECK(v[1].has_value() == false); 3833 CHECK(v[2].has_value()); 3834 CHECK(v[2].value() == 7); 3835 CHECK(v[3].has_value() == false); 3836 CHECK(v[4].has_value()); 3837 CHECK(v[4].value() == 9); 3838 3839 // readout with statement preparation 3840 3841 int id = 1; 3842 3843 ids.resize(3); 3844 v.resize(3); 3845 statement st = (sql.prepare << 3846 "select id, val from soci_test order by id", into(ids), into(v)); 3847 st.execute(); 3848 while (st.fetch()) 3849 { 3850 for (std::size_t i = 0; i != v.size(); ++i) 3851 { 3852 CHECK(id == ids[i]); 3853 3854 if (id == 2 || id == 4) 3855 { 3856 CHECK(v[i].has_value() == false); 3857 } 3858 else 3859 { 3860 CHECK(v[i].has_value()); 3861 CHECK(v[i].value() == id + 4); 3862 } 3863 3864 ++id; 3865 } 3866 3867 ids.resize(3); 3868 v.resize(3); 3869 } 3870 CHECK(id == 6); 3871 } 3872 3873 // and why not stress iterators and the dynamic binding, too! 3874 3875 { 3876 rowset<row> rs = (sql.prepare << "select id, val, str from soci_test order by id"); 3877 3878 rowset<row>::const_iterator it = rs.begin(); 3879 CHECK(it != rs.end()); 3880 3881 row const& r1 = (*it); 3882 3883 CHECK(r1.size() == 3); 3884 3885 // Note: for the reason of differences between number(x,y) type and 3886 // binary representation of integers, the following commented assertions 3887 // do not work for Oracle. 3888 // The problem is that for this single table the data type used in Oracle 3889 // table creator for the id column is number(10,0), 3890 // which allows to insert all int values. 3891 // On the other hand, the column description scheme used in the Oracle 3892 // backend figures out that the natural type for such a column 3893 // is eUnsignedInt - this makes the following assertions fail. 3894 // Other database backends (like PostgreSQL) use other types like int 3895 // and this not only allows to insert all int values (obviously), 3896 // but is also recognized as int (obviously). 3897 // There is a similar problem with stream-like extraction, 3898 // where internally get<T> is called and the type mismatch is detected 3899 // for the id column - that's why the code below skips this column 3900 // and tests the remaining column only. 3901 3902 //CHECK(r1.get_properties(0).get_data_type() == dt_integer); 3903 CHECK(r1.get_properties(1).get_data_type() == dt_integer); 3904 CHECK(r1.get_properties(2).get_data_type() == dt_string); 3905 //CHECK(r1.get<int>(0) == 1); 3906 CHECK(r1.get<int>(1) == 5); 3907 CHECK(r1.get<std::string>(2) == "abc"); 3908 CHECK(r1.get<std::optional<int> >(1).has_value()); 3909 CHECK(r1.get<std::optional<int> >(1).value() == 5); 3910 CHECK(r1.get<std::optional<std::string> >(2).has_value()); 3911 CHECK(r1.get<std::optional<std::string> >(2).value() == "abc"); 3912 3913 ++it; 3914 3915 row const& r2 = (*it); 3916 3917 CHECK(r2.size() == 3); 3918 3919 // CHECK(r2.get_properties(0).get_data_type() == dt_integer); 3920 CHECK(r2.get_properties(1).get_data_type() == dt_integer); 3921 CHECK(r2.get_properties(2).get_data_type() == dt_string); 3922 //CHECK(r2.get<int>(0) == 2); 3923 try 3924 { 3925 // expect exception here, this is NULL value 3926 (void)r1.get<int>(1); 3927 FAIL("expected exception not thrown"); 3928 } 3929 catch (soci_error const &) {} 3930 3931 // but we can read it as optional 3932 CHECK(r2.get<std::optional<int> >(1).has_value() == false); 3933 3934 // stream-like data extraction 3935 3936 ++it; 3937 row const &r3 = (*it); 3938 3939 std::optional<int> io; 3940 std::optional<std::string> so; 3941 3942 r3.skip(); // move to val and str columns 3943 r3 >> io >> so; 3944 3945 CHECK(io.has_value()); 3946 CHECK(io.value() == 7); 3947 CHECK(so.has_value()); 3948 CHECK(so.value() == "ghi"); 3949 3950 ++it; 3951 row const &r4 = (*it); 3952 3953 r3.skip(); // move to val and str columns 3954 r4 >> io >> so; 3955 3956 CHECK(io.has_value() == false); 3957 CHECK(so.has_value() == false); 3958 } 3959 3960 // inserts of non-null const data 3961 { 3962 sql << "delete from soci_test"; 3963 3964 const int id = 10; 3965 const std::optional<int> val = 11; 3966 3967 sql << "insert into soci_test(id, val) values(:id, :val)", 3968 use(id, "id"), 3969 use(val, "val"); 3970 3971 int sum; 3972 sql << "select sum(val) from soci_test", into(sum); 3973 CHECK(sum == 11); 3974 } 3975 3976 // bulk inserts of non-null data 3977 3978 { 3979 sql << "delete from soci_test"; 3980 3981 std::vector<int> ids; 3982 std::vector<std::optional<int> > v; 3983 3984 ids.push_back(10); v.push_back(20); 3985 ids.push_back(11); v.push_back(21); 3986 ids.push_back(12); v.push_back(22); 3987 ids.push_back(13); v.push_back(23); 3988 3989 sql << "insert into soci_test(id, val) values(:id, :val)", 3990 use(ids, "id"), use(v, "val"); 3991 3992 int sum; 3993 sql << "select sum(val) from soci_test", into(sum); 3994 CHECK(sum == 86); 3995 3996 // bulk inserts of some-null data 3997 3998 sql << "delete from soci_test"; 3999 4000 v[2].reset(); 4001 v[3].reset(); 4002 4003 sql << "insert into soci_test(id, val) values(:id, :val)", 4004 use(ids, "id"), use(v, "val"); 4005 4006 sql << "select sum(val) from soci_test", into(sum); 4007 CHECK(sum == 41); 4008 } 4009 4010 4011 // bulk inserts of non-null data with const vector 4012 4013 { 4014 sql << "delete from soci_test"; 4015 4016 std::vector<int> ids; 4017 std::vector<std::optional<int> > v; 4018 4019 ids.push_back(10); v.push_back(20); 4020 ids.push_back(11); v.push_back(21); 4021 ids.push_back(12); v.push_back(22); 4022 ids.push_back(13); v.push_back(23); 4023 4024 const std::vector<int>& cref_ids = ids; 4025 const std::vector<std::optional<int> >& cref_v = v; 4026 4027 sql << "insert into soci_test(id, val) values(:id, :val)", 4028 use(cref_ids, "id"), 4029 use(cref_v, "val"); 4030 4031 int sum; 4032 sql << "select sum(val) from soci_test", into(sum); 4033 CHECK(sum == 86); 4034 4035 // bulk inserts of some-null data 4036 4037 sql << "delete from soci_test"; 4038 4039 v[2].reset(); 4040 v[3].reset(); 4041 4042 sql << "insert into soci_test(id, val) values(:id, :val)", 4043 use(cref_ids, "id"), 4044 use(cref_v, "val"); 4045 4046 sql << "select sum(val) from soci_test", into(sum); 4047 CHECK(sum == 41); 4048 } 4049 4050 // composability with user conversions 4051 4052 { 4053 sql << "delete from soci_test"; 4054 4055 std::optional<MyInt> omi1; 4056 std::optional<MyInt> omi2; 4057 4058 omi1 = MyInt(125); 4059 omi2.reset(); 4060 4061 sql << "insert into soci_test(id, val) values(:id, :val)", 4062 use(omi1), use(omi2); 4063 4064 sql << "select id, val from soci_test", into(omi2), into(omi1); 4065 4066 CHECK(omi1.has_value() == false); 4067 CHECK(omi2.has_value()); 4068 CHECK(omi2.value().get() == 125); 4069 } 4070 4071 // use with const optional and user conversions 4072 4073 { 4074 sql << "delete from soci_test"; 4075 4076 std::optional<MyInt> omi1; 4077 std::optional<MyInt> omi2; 4078 4079 omi1 = MyInt(125); 4080 omi2.reset(); 4081 4082 std::optional<MyInt> const & comi1 = omi1; 4083 std::optional<MyInt> const & comi2 = omi2; 4084 4085 sql << "insert into soci_test(id, val) values(:id, :val)", 4086 use(comi1), use(comi2); 4087 4088 sql << "select id, val from soci_test", into(omi2), into(omi1); 4089 4090 CHECK(omi1.has_value() == false); 4091 CHECK(omi2.has_value()); 4092 CHECK(omi2.value().get() == 125); 4093 } 4094 4095 // use with rowset and table containing null values 4096 4097 { 4098 auto_table_creator tableCreator(tc_.table_creator_1(sql)); 4099 4100 sql << "insert into soci_test(id, val) values(1, 10)"; 4101 sql << "insert into soci_test(id, val) values(2, 11)"; 4102 sql << "insert into soci_test(id, val) values(3, NULL)"; 4103 sql << "insert into soci_test(id, val) values(4, 13)"; 4104 4105 rowset<std::optional<int> > rs = (sql.prepare << 4106 "select val from soci_test order by id asc"); 4107 4108 // 1st row 4109 rowset<std::optional<int> >::const_iterator pos = rs.begin(); 4110 CHECK((*pos).has_value()); 4111 CHECK(10 == (*pos).value()); 4112 4113 // 2nd row 4114 ++pos; 4115 CHECK((*pos).has_value()); 4116 CHECK(11 == (*pos).value()); 4117 4118 // 3rd row 4119 ++pos; 4120 CHECK((*pos).has_value() == false); 4121 4122 // 4th row 4123 ++pos; 4124 CHECK((*pos).has_value()); 4125 CHECK(13 == (*pos).value()); 4126 } 4127 4128 // inserting using an i_null indicator with a std::optional should 4129 // insert null, even if the optional is valid, just as with standard 4130 // types 4131 { 4132 auto_table_creator tableCreator(tc_.table_creator_1(sql)); 4133 4134 { 4135 indicator ind = i_null; 4136 std::optional<int> v1(10); 4137 sql << "insert into soci_test(id, val) values(1, :val)", 4138 use(v1, ind); 4139 } 4140 4141 // verify the value is fetched correctly as null 4142 { 4143 indicator ind; 4144 std::optional<int> opt; 4145 4146 ind = i_truncated; 4147 opt = 0; 4148 sql << "select val from soci_test where id = 1", into(opt, ind); 4149 CHECK(ind == i_null); 4150 CHECK(!opt.has_value()); 4151 } 4152 } 4153 4154 // prepared statement inserting non-null and null values alternatively 4155 // (without passing an explicit indicator) 4156 { 4157 auto_table_creator tableCreator(tc_.table_creator_1(sql)); 4158 4159 { 4160 int id; 4161 std::optional<int> val; 4162 statement st = (sql.prepare 4163 << "insert into soci_test(id, val) values (:id, :val)", 4164 use(id), use(val)); 4165 4166 id = 1; 4167 val = 10; 4168 st.execute(true); 4169 4170 id = 2; 4171 val = std::optional<int>(); 4172 st.execute(true); 4173 4174 id = 3; 4175 val = 11; 4176 st.execute(true); 4177 } 4178 4179 // verify values are fetched correctly 4180 { 4181 indicator ind; 4182 std::optional<int> opt; 4183 4184 ind = i_truncated; 4185 opt = 0; 4186 sql << "select val from soci_test where id = 1", into(opt, ind); 4187 CHECK(ind == i_ok); 4188 CHECK(opt.has_value()); 4189 CHECK(opt.value() == 10); 4190 4191 ind = i_truncated; 4192 opt = 0; 4193 sql << "select val from soci_test where id = 2", into(opt, ind); 4194 CHECK(ind == i_null); 4195 CHECK(!opt.has_value()); 4196 4197 ind = i_truncated; 4198 opt = 0; 4199 sql << "select val from soci_test where id = 3", into(opt, ind); 4200 CHECK(ind == i_ok); 4201 REQUIRE(opt.has_value()); 4202 CHECK(opt.value() == 11); 4203 } 4204 } 4205 } 4206 } 4207 #endif 4208 4209 // connection and reconnection tests 4210 TEST_CASE_METHOD(common_tests, "Connection and reconnection", "[core][connect]") 4211 { 4212 { 4213 // empty session 4214 soci::session sql; 4215 4216 CHECK(!sql.is_connected()); 4217 4218 // idempotent: 4219 sql.close(); 4220 4221 try 4222 { 4223 sql.reconnect(); 4224 FAIL("expected exception not thrown"); 4225 } 4226 catch (soci_error const &e) 4227 { 4228 CHECK(e.get_error_message() == 4229 "Cannot reconnect without previous connection."); 4230 } 4231 4232 // open from empty session 4233 sql.open(backEndFactory_, connectString_); 4234 CHECK(sql.is_connected()); 4235 sql.close(); 4236 CHECK(!sql.is_connected()); 4237 4238 // reconnecting from closed session 4239 sql.reconnect(); 4240 CHECK(sql.is_connected()); 4241 4242 // opening already connected session 4243 try 4244 { 4245 sql.open(backEndFactory_, connectString_); 4246 FAIL("expected exception not thrown"); 4247 } 4248 catch (soci_error const &e) 4249 { 4250 CHECK(e.get_error_message() == 4251 "Cannot open already connected session."); 4252 } 4253 4254 CHECK(sql.is_connected()); 4255 sql.close(); 4256 4257 // open from closed 4258 sql.open(backEndFactory_, connectString_); 4259 CHECK(sql.is_connected()); 4260 4261 // reconnect from already connected session 4262 sql.reconnect(); 4263 CHECK(sql.is_connected()); 4264 } 4265 4266 { 4267 soci::session sql; 4268 4269 try 4270 { 4271 sql << "this statement cannot execute"; 4272 FAIL("expected exception not thrown"); 4273 } 4274 catch (soci_error const &e) 4275 { 4276 CHECK(e.get_error_message() == 4277 "Session is not connected."); 4278 } 4279 } 4280 4281 } 4282 4283 #ifdef SOCI_HAVE_BOOST 4284 4285 TEST_CASE_METHOD(common_tests, "Boost tuple", "[core][boost][tuple]") 4286 { 4287 soci::session sql(backEndFactory_, connectString_); 4288 4289 auto_table_creator tableCreator(tc_.table_creator_2(sql)); 4290 { 4291 boost::tuple<double, int, std::string> t1(3.5, 7, "Joe Hacker"); 4292 ASSERT_EQUAL(t1.get<0>(), 3.5); 4293 CHECK(t1.get<1>() == 7); 4294 CHECK(t1.get<2>() == "Joe Hacker"); 4295 4296 sql << "insert into soci_test(num_float, num_int, name) values(:d, :i, :s)", use(t1); 4297 4298 // basic query 4299 4300 boost::tuple<double, int, std::string> t2; 4301 sql << "select num_float, num_int, name from soci_test", into(t2); 4302 4303 ASSERT_EQUAL(t2.get<0>(), 3.5); 4304 CHECK(t2.get<1>() == 7); 4305 CHECK(t2.get<2>() == "Joe Hacker"); 4306 4307 sql << "delete from soci_test"; 4308 } 4309 4310 { 4311 // composability with boost::optional 4312 4313 // use: 4314 boost::tuple<double, boost::optional<int>, std::string> t1( 4315 3.5, boost::optional<int>(7), "Joe Hacker"); 4316 ASSERT_EQUAL(t1.get<0>(), 3.5); 4317 CHECK(t1.get<1>().is_initialized()); 4318 CHECK(t1.get<1>().get() == 7); 4319 CHECK(t1.get<2>() == "Joe Hacker"); 4320 4321 sql << "insert into soci_test(num_float, num_int, name) values(:d, :i, :s)", use(t1); 4322 4323 // into: 4324 boost::tuple<double, boost::optional<int>, std::string> t2; 4325 sql << "select num_float, num_int, name from soci_test", into(t2); 4326 4327 ASSERT_EQUAL(t2.get<0>(), 3.5); 4328 CHECK(t2.get<1>().is_initialized()); 4329 CHECK(t2.get<1>().get() == 7); 4330 CHECK(t2.get<2>() == "Joe Hacker"); 4331 4332 sql << "delete from soci_test"; 4333 } 4334 4335 { 4336 // composability with user-provided conversions 4337 4338 // use: 4339 boost::tuple<double, MyInt, std::string> t1(3.5, 7, "Joe Hacker"); 4340 ASSERT_EQUAL(t1.get<0>(), 3.5); 4341 CHECK(t1.get<1>().get() == 7); 4342 CHECK(t1.get<2>() == "Joe Hacker"); 4343 4344 sql << "insert into soci_test(num_float, num_int, name) values(:d, :i, :s)", use(t1); 4345 4346 // into: 4347 boost::tuple<double, MyInt, std::string> t2; 4348 4349 sql << "select num_float, num_int, name from soci_test", into(t2); 4350 4351 ASSERT_EQUAL(t2.get<0>(), 3.5); 4352 CHECK(t2.get<1>().get() == 7); 4353 CHECK(t2.get<2>() == "Joe Hacker"); 4354 4355 sql << "delete from soci_test"; 4356 } 4357 4358 { 4359 // let's have fun - composition of tuple, optional and user-defined type 4360 4361 // use: 4362 boost::tuple<double, boost::optional<MyInt>, std::string> t1( 4363 3.5, boost::optional<MyInt>(7), "Joe Hacker"); 4364 ASSERT_EQUAL(t1.get<0>(), 3.5); 4365 CHECK(t1.get<1>().is_initialized()); 4366 CHECK(t1.get<1>().get().get() == 7); 4367 CHECK(t1.get<2>() == "Joe Hacker"); 4368 4369 sql << "insert into soci_test(num_float, num_int, name) values(:d, :i, :s)", use(t1); 4370 4371 // into: 4372 boost::tuple<double, boost::optional<MyInt>, std::string> t2; 4373 4374 sql << "select num_float, num_int, name from soci_test", into(t2); 4375 4376 ASSERT_EQUAL(t2.get<0>(), 3.5); 4377 CHECK(t2.get<1>().is_initialized()); 4378 CHECK(t2.get<1>().get().get() == 7); 4379 CHECK(t2.get<2>() == "Joe Hacker"); 4380 4381 sql << "update soci_test set num_int = NULL"; 4382 4383 sql << "select num_float, num_int, name from soci_test", into(t2); 4384 4385 ASSERT_EQUAL(t2.get<0>(), 3.5); 4386 CHECK(t2.get<1>().is_initialized() == false); 4387 CHECK(t2.get<2>() == "Joe Hacker"); 4388 } 4389 4390 { 4391 // rowset<tuple> 4392 4393 sql << "insert into soci_test(num_float, num_int, name) values(4.0, 8, 'Tony Coder')"; 4394 sql << "insert into soci_test(num_float, num_int, name) values(4.5, NULL, 'Cecile Sharp')"; 4395 sql << "insert into soci_test(num_float, num_int, name) values(5.0, 10, 'Djhava Ravaa')"; 4396 4397 typedef boost::tuple<double, boost::optional<int>, std::string> T; 4398 4399 rowset<T> rs = (sql.prepare 4400 << "select num_float, num_int, name from soci_test order by num_float asc"); 4401 4402 rowset<T>::const_iterator pos = rs.begin(); 4403 4404 ASSERT_EQUAL(pos->get<0>(), 3.5); 4405 CHECK(pos->get<1>().is_initialized() == false); 4406 CHECK(pos->get<2>() == "Joe Hacker"); 4407 4408 ++pos; 4409 ASSERT_EQUAL(pos->get<0>(), 4.0); 4410 CHECK(pos->get<1>().is_initialized()); 4411 CHECK(pos->get<1>().get() == 8); 4412 CHECK(pos->get<2>() == "Tony Coder"); 4413 4414 ++pos; 4415 ASSERT_EQUAL(pos->get<0>(), 4.5); 4416 CHECK(pos->get<1>().is_initialized() == false); 4417 CHECK(pos->get<2>() == "Cecile Sharp"); 4418 4419 ++pos; 4420 ASSERT_EQUAL(pos->get<0>(), 5.0); 4421 CHECK(pos->get<1>().is_initialized()); 4422 CHECK(pos->get<1>().get() == 10); 4423 CHECK(pos->get<2>() == "Djhava Ravaa"); 4424 4425 ++pos; 4426 CHECK(pos == rs.end()); 4427 } 4428 } 4429 4430 #if defined(BOOST_VERSION) && BOOST_VERSION >= 103500 4431 4432 TEST_CASE_METHOD(common_tests, "Boost fusion", "[core][boost][fusion]") 4433 { 4434 4435 soci::session sql(backEndFactory_, connectString_); 4436 4437 auto_table_creator tableCreator(tc_.table_creator_2(sql)); 4438 { 4439 boost::fusion::vector<double, int, std::string> t1(3.5, 7, "Joe Hacker"); 4440 ASSERT_EQUAL(boost::fusion::at_c<0>(t1), 3.5); 4441 CHECK(boost::fusion::at_c<1>(t1) == 7); 4442 CHECK(boost::fusion::at_c<2>(t1) == "Joe Hacker"); 4443 4444 sql << "insert into soci_test(num_float, num_int, name) values(:d, :i, :s)", use(t1); 4445 4446 // basic query 4447 4448 boost::fusion::vector<double, int, std::string> t2; 4449 sql << "select num_float, num_int, name from soci_test", into(t2); 4450 4451 ASSERT_EQUAL(boost::fusion::at_c<0>(t2), 3.5); 4452 CHECK(boost::fusion::at_c<1>(t2) == 7); 4453 CHECK(boost::fusion::at_c<2>(t2) == "Joe Hacker"); 4454 4455 sql << "delete from soci_test"; 4456 } 4457 4458 { 4459 // composability with boost::optional 4460 4461 // use: 4462 boost::fusion::vector<double, boost::optional<int>, std::string> t1( 4463 3.5, boost::optional<int>(7), "Joe Hacker"); 4464 ASSERT_EQUAL(boost::fusion::at_c<0>(t1), 3.5); 4465 CHECK(boost::fusion::at_c<1>(t1).is_initialized()); 4466 CHECK(boost::fusion::at_c<1>(t1).get() == 7); 4467 CHECK(boost::fusion::at_c<2>(t1) == "Joe Hacker"); 4468 4469 sql << "insert into soci_test(num_float, num_int, name) values(:d, :i, :s)", use(t1); 4470 4471 // into: 4472 boost::fusion::vector<double, boost::optional<int>, std::string> t2; 4473 sql << "select num_float, num_int, name from soci_test", into(t2); 4474 4475 ASSERT_EQUAL(boost::fusion::at_c<0>(t2), 3.5); 4476 CHECK(boost::fusion::at_c<1>(t2).is_initialized()); 4477 CHECK(boost::fusion::at_c<1>(t2) == 7); 4478 CHECK(boost::fusion::at_c<2>(t2) == "Joe Hacker"); 4479 4480 sql << "delete from soci_test"; 4481 } 4482 4483 { 4484 // composability with user-provided conversions 4485 4486 // use: 4487 boost::fusion::vector<double, MyInt, std::string> t1(3.5, 7, "Joe Hacker"); 4488 ASSERT_EQUAL(boost::fusion::at_c<0>(t1), 3.5); 4489 CHECK(boost::fusion::at_c<1>(t1).get() == 7); 4490 CHECK(boost::fusion::at_c<2>(t1) == "Joe Hacker"); 4491 4492 sql << "insert into soci_test(num_float, num_int, name) values(:d, :i, :s)", use(t1); 4493 4494 // into: 4495 boost::fusion::vector<double, MyInt, std::string> t2; 4496 4497 sql << "select num_float, num_int, name from soci_test", into(t2); 4498 4499 ASSERT_EQUAL(boost::fusion::at_c<0>(t2), 3.5); 4500 CHECK(boost::fusion::at_c<1>(t2).get() == 7); 4501 CHECK(boost::fusion::at_c<2>(t2) == "Joe Hacker"); 4502 4503 sql << "delete from soci_test"; 4504 } 4505 4506 { 4507 // let's have fun - composition of tuple, optional and user-defined type 4508 4509 // use: 4510 boost::fusion::vector<double, boost::optional<MyInt>, std::string> t1( 4511 3.5, boost::optional<MyInt>(7), "Joe Hacker"); 4512 ASSERT_EQUAL(boost::fusion::at_c<0>(t1), 3.5); 4513 CHECK(boost::fusion::at_c<1>(t1).is_initialized()); 4514 CHECK(boost::fusion::at_c<1>(t1).get().get() == 7); 4515 CHECK(boost::fusion::at_c<2>(t1) == "Joe Hacker"); 4516 4517 sql << "insert into soci_test(num_float, num_int, name) values(:d, :i, :s)", use(t1); 4518 4519 // into: 4520 boost::fusion::vector<double, boost::optional<MyInt>, std::string> t2; 4521 4522 sql << "select num_float, num_int, name from soci_test", into(t2); 4523 4524 ASSERT_EQUAL(boost::fusion::at_c<0>(t2), 3.5); 4525 CHECK(boost::fusion::at_c<1>(t2).is_initialized()); 4526 CHECK(boost::fusion::at_c<1>(t2).get().get() == 7); 4527 CHECK(boost::fusion::at_c<2>(t2) == "Joe Hacker"); 4528 4529 sql << "update soci_test set num_int = NULL"; 4530 4531 sql << "select num_float, num_int, name from soci_test", into(t2); 4532 4533 ASSERT_EQUAL(boost::fusion::at_c<0>(t2), 3.5); 4534 CHECK(boost::fusion::at_c<1>(t2).is_initialized() == false); 4535 CHECK(boost::fusion::at_c<2>(t2) == "Joe Hacker"); 4536 } 4537 4538 { 4539 // rowset<fusion::vector> 4540 4541 sql << "insert into soci_test(num_float, num_int, name) values(4.0, 8, 'Tony Coder')"; 4542 sql << "insert into soci_test(num_float, num_int, name) values(4.5, NULL, 'Cecile Sharp')"; 4543 sql << "insert into soci_test(num_float, num_int, name) values(5.0, 10, 'Djhava Ravaa')"; 4544 4545 typedef boost::fusion::vector<double, boost::optional<int>, std::string> T; 4546 4547 rowset<T> rs = (sql.prepare 4548 << "select num_float, num_int, name from soci_test order by num_float asc"); 4549 4550 rowset<T>::const_iterator pos = rs.begin(); 4551 4552 ASSERT_EQUAL(boost::fusion::at_c<0>(*pos), 3.5); 4553 CHECK(boost::fusion::at_c<1>(*pos).is_initialized() == false); 4554 CHECK(boost::fusion::at_c<2>(*pos) == "Joe Hacker"); 4555 4556 ++pos; 4557 ASSERT_EQUAL(boost::fusion::at_c<0>(*pos), 4.0); 4558 CHECK(boost::fusion::at_c<1>(*pos).is_initialized()); 4559 CHECK(boost::fusion::at_c<1>(*pos).get() == 8); 4560 CHECK(boost::fusion::at_c<2>(*pos) == "Tony Coder"); 4561 4562 ++pos; 4563 ASSERT_EQUAL(boost::fusion::at_c<0>(*pos), 4.5); 4564 CHECK(boost::fusion::at_c<1>(*pos).is_initialized() == false); 4565 CHECK(boost::fusion::at_c<2>(*pos) == "Cecile Sharp"); 4566 4567 ++pos; 4568 ASSERT_EQUAL(boost::fusion::at_c<0>(*pos), 5.0); 4569 CHECK(boost::fusion::at_c<1>(*pos).is_initialized()); 4570 CHECK(boost::fusion::at_c<1>(*pos).get() == 10); 4571 CHECK(boost::fusion::at_c<2>(*pos) == "Djhava Ravaa"); 4572 4573 ++pos; 4574 CHECK(pos == rs.end()); 4575 } 4576 } 4577 4578 #endif // defined(BOOST_VERSION) && BOOST_VERSION >= 103500 4579 4580 // test for boost::gregorian::date 4581 TEST_CASE_METHOD(common_tests, "Boost date", "[core][boost][datetime]") 4582 { 4583 soci::session sql(backEndFactory_, connectString_); 4584 4585 { 4586 auto_table_creator tableCreator(tc_.table_creator_1(sql)); 4587 4588 std::tm nov15 = std::tm(); 4589 nov15.tm_year = 105; 4590 nov15.tm_mon = 10; 4591 nov15.tm_mday = 15; 4592 nov15.tm_hour = 0; 4593 nov15.tm_min = 0; 4594 nov15.tm_sec = 0; 4595 4596 sql << "insert into soci_test(tm) values(:tm)", use(nov15); 4597 4598 boost::gregorian::date bgd; 4599 sql << "select tm from soci_test", into(bgd); 4600 4601 CHECK(bgd.year() == 2005); 4602 CHECK(bgd.month() == 11); 4603 CHECK(bgd.day() == 15); 4604 4605 sql << "update soci_test set tm = NULL"; 4606 try 4607 { 4608 sql << "select tm from soci_test", into(bgd); 4609 FAIL("expected exception not thrown"); 4610 } 4611 catch (soci_error const & e) 4612 { 4613 CHECK(e.get_error_message() == 4614 "Null value not allowed for this type"); 4615 } 4616 } 4617 4618 { 4619 auto_table_creator tableCreator(tc_.table_creator_1(sql)); 4620 4621 boost::gregorian::date bgd(2008, boost::gregorian::May, 5); 4622 4623 sql << "insert into soci_test(tm) values(:tm)", use(bgd); 4624 4625 std::tm t = std::tm(); 4626 sql << "select tm from soci_test", into(t); 4627 4628 CHECK(t.tm_year == 108); 4629 CHECK(t.tm_mon == 4); 4630 CHECK(t.tm_mday == 5); 4631 } 4632 4633 } 4634 4635 #endif // SOCI_HAVE_BOOST 4636 4637 // connection pool - simple sequential test, no multiple threads 4638 TEST_CASE_METHOD(common_tests, "Connection pool", "[core][connection][pool]") 4639 { 4640 // phase 1: preparation 4641 const size_t pool_size = 10; 4642 connection_pool pool(pool_size); 4643 4644 for (std::size_t i = 0; i != pool_size; ++i) 4645 { 4646 session & sql = pool.at(i); 4647 sql.open(backEndFactory_, connectString_); 4648 } 4649 4650 // phase 2: usage 4651 for (std::size_t i = 0; i != pool_size; ++i) 4652 { 4653 // poor man way to lease more than one connection 4654 soci::session sql_unused1(pool); 4655 soci::session sql(pool); 4656 soci::session sql_unused2(pool); 4657 { 4658 auto_table_creator tableCreator(tc_.table_creator_1(sql)); 4659 4660 char c('a'); 4661 sql << "insert into soci_test(c) values(:c)", use(c); 4662 sql << "select c from soci_test", into(c); 4663 CHECK(c == 'a'); 4664 } 4665 } 4666 } 4667 4668 // Issue 66 - test query transformation callback feature 4669 static std::string no_op_transform(std::string query) 4670 { 4671 return query; 4672 } 4673 4674 static std::string lower_than_g(std::string query) 4675 { 4676 return query + " WHERE c < 'g'"; 4677 } 4678 4679 struct where_condition 4680 { 4681 where_condition(std::string const& where) 4682 : where_(where) 4683 {} 4684 4685 std::string operator()(std::string const& query) const 4686 { 4687 return query + " WHERE " + where_; 4688 } 4689 4690 std::string where_; 4691 }; 4692 4693 4694 void run_query_transformation_test(test_context_base const& tc, session& sql) 4695 { 4696 // create and populate the test table 4697 auto_table_creator tableCreator(tc.table_creator_1(sql)); 4698 4699 for (char c = 'a'; c <= 'z'; ++c) 4700 { 4701 sql << "insert into soci_test(c) values(\'" << c << "\')"; 4702 } 4703 4704 char const* query = "select count(*) from soci_test"; 4705 4706 // free function, no-op 4707 { 4708 sql.set_query_transformation(no_op_transform); 4709 int count; 4710 sql << query, into(count); 4711 CHECK(count == 'z' - 'a' + 1); 4712 } 4713 4714 // free function 4715 { 4716 sql.set_query_transformation(lower_than_g); 4717 int count; 4718 sql << query, into(count); 4719 CHECK(count == 'g' - 'a'); 4720 } 4721 4722 // function object with state 4723 { 4724 sql.set_query_transformation(where_condition("c > 'g' AND c < 'j'")); 4725 int count = 0; 4726 sql << query, into(count); 4727 CHECK(count == 'j' - 'h'); 4728 count = 0; 4729 sql.set_query_transformation(where_condition("c > 's' AND c <= 'z'")); 4730 sql << query, into(count); 4731 CHECK(count == 'z' - 's'); 4732 } 4733 4734 #if 0 4735 // lambda is just presented as an example to curious users 4736 { 4737 sql.set_query_transformation( 4738 [](std::string const& query) { 4739 return query + " WHERE c > 'g' AND c < 'j'"; 4740 }); 4741 4742 int count = 0; 4743 sql << query, into(count); 4744 CHECK(count == 'j' - 'h'); 4745 } 4746 #endif 4747 4748 // prepared statements 4749 4750 // constant effect (pre-prepare set transformation) 4751 { 4752 // set transformation after statement is prepared 4753 sql.set_query_transformation(lower_than_g); 4754 // prepare statement 4755 int count; 4756 statement st = (sql.prepare << query, into(count)); 4757 // observe transformation effect 4758 st.execute(true); 4759 CHECK(count == 'g' - 'a'); 4760 // reset transformation 4761 sql.set_query_transformation(no_op_transform); 4762 // observe the same transformation, no-op set above has no effect 4763 count = 0; 4764 st.execute(true); 4765 CHECK(count == 'g' - 'a'); 4766 } 4767 4768 // no effect (post-prepare set transformation) 4769 { 4770 // reset 4771 sql.set_query_transformation(no_op_transform); 4772 4773 // prepare statement 4774 int count; 4775 statement st = (sql.prepare << query, into(count)); 4776 // set transformation after statement is prepared 4777 sql.set_query_transformation(lower_than_g); 4778 // observe no effect of WHERE clause injection 4779 st.execute(true); 4780 CHECK(count == 'z' - 'a' + 1); 4781 } 4782 } 4783 4784 TEST_CASE_METHOD(common_tests, "Query transformation", "[core][query-transform]") 4785 { 4786 soci::session sql(backEndFactory_, connectString_); 4787 run_query_transformation_test(tc_, sql); 4788 } 4789 4790 TEST_CASE_METHOD(common_tests, "Query transformation with connection pool", "[core][query-transform][pool]") 4791 { 4792 // phase 1: preparation 4793 const size_t pool_size = 10; 4794 connection_pool pool(pool_size); 4795 4796 for (std::size_t i = 0; i != pool_size; ++i) 4797 { 4798 session & sql = pool.at(i); 4799 sql.open(backEndFactory_, connectString_); 4800 } 4801 4802 soci::session sql(pool); 4803 run_query_transformation_test(tc_, sql); 4804 } 4805 4806 // Originally, submitted to SQLite3 backend and later moved to common test. 4807 // Test commit b394d039530f124802d06c3b1a969c3117683152 4808 // Author: Mika Fischer <mika.fischer@zoopnet.de> 4809 // Date: Thu Nov 17 13:28:07 2011 +0100 4810 // Implement get_affected_rows for SQLite3 backend 4811 TEST_CASE_METHOD(common_tests, "Get affected rows", "[core][affected-rows]") 4812 { 4813 soci::session sql(backEndFactory_, connectString_); 4814 auto_table_creator tableCreator(tc_.table_creator_4(sql)); 4815 if (!tableCreator.get()) 4816 { 4817 std::cout << "test get_affected_rows skipped (function not implemented)" << std::endl; 4818 return; 4819 } 4820 4821 4822 for (int i = 0; i != 10; i++) 4823 { 4824 sql << "insert into soci_test(val) values(:val)", use(i); 4825 } 4826 4827 int step = 2; 4828 statement st1 = (sql.prepare << 4829 "update soci_test set val = val + :step where val = 5", use(step, "step")); 4830 st1.execute(true); 4831 CHECK(st1.get_affected_rows() == 1); 4832 4833 // attempts to run the query again, no rows should be affected 4834 st1.execute(true); 4835 CHECK(st1.get_affected_rows() == 0); 4836 4837 statement st2 = (sql.prepare << 4838 "update soci_test set val = val + 1"); 4839 st2.execute(true); 4840 4841 CHECK(st2.get_affected_rows() == 10); 4842 4843 statement st3 = (sql.prepare << 4844 "delete from soci_test where val <= 5"); 4845 st3.execute(true); 4846 4847 CHECK(st3.get_affected_rows() == 5); 4848 4849 statement st4 = (sql.prepare << 4850 "update soci_test set val = val + 1"); 4851 st4.execute(true); 4852 4853 CHECK(st4.get_affected_rows() == 5); 4854 4855 std::vector<int> v(5, 0); 4856 for (std::size_t i = 0; i < v.size(); ++i) 4857 { 4858 v[i] = (7 + static_cast<int>(i)); 4859 } 4860 4861 // test affected rows for bulk operations. 4862 statement st5 = (sql.prepare << 4863 "delete from soci_test where val = :v", use(v)); 4864 st5.execute(true); 4865 4866 CHECK(st5.get_affected_rows() == 5); 4867 4868 std::vector<std::string> w(2, "1"); 4869 w[1] = "a"; // this invalid value may cause an exception. 4870 statement st6 = (sql.prepare << 4871 "insert into soci_test(val) values(:val)", use(w)); 4872 try { st6.execute(true); } 4873 catch(...) {} 4874 4875 // confirm the partial insertion. 4876 int val = 0; 4877 sql << "select count(val) from soci_test", into(val); 4878 if(val != 0) 4879 { 4880 // Notice that some ODBC drivers don't return the number of updated 4881 // rows at all in the case of partially executed statement like this 4882 // one, while MySQL ODBC driver wrongly returns 2 affected rows even 4883 // though only one was actually inserted. 4884 // 4885 // So we can't check for "get_affected_rows() == val" here, it would 4886 // fail in too many cases -- just check that the backend doesn't lie to 4887 // us about no rows being affected at all (even if it just honestly 4888 // admits that it has no idea by returning -1). 4889 CHECK(st6.get_affected_rows() != 0); 4890 } 4891 } 4892 4893 // test fix for: Backend is not set properly with connection pool (pull #5) 4894 TEST_CASE_METHOD(common_tests, "Backend with connection pool", "[core][pool]") 4895 { 4896 const size_t pool_size = 1; 4897 connection_pool pool(pool_size); 4898 4899 for (std::size_t i = 0; i != pool_size; ++i) 4900 { 4901 session & sql = pool.at(i); 4902 sql.open(backEndFactory_, connectString_); 4903 } 4904 4905 soci::session sql(pool); 4906 sql.reconnect(); 4907 sql.begin(); // no crash expected 4908 } 4909 4910 // test fix for: Session from connection pool not set backend properly when call open 4911 TEST_CASE_METHOD(common_tests, "Session from connection pool call open reset backend", "[core][pool]") 4912 { 4913 const size_t pool_size = 1; 4914 connection_pool pool(pool_size); 4915 4916 soci::session sql(pool); 4917 sql.open(backEndFactory_, connectString_); 4918 REQUIRE_NOTHROW( sql.begin() ); 4919 } 4920 4921 // issue 67 - Allocated statement backend memory leaks on exception 4922 // If the test runs under memory debugger and it passes, then 4923 // soci::details::statement_impl::backEnd_ must not leak 4924 TEST_CASE_METHOD(common_tests, "Backend memory leak", "[core][leak]") 4925 { 4926 soci::session sql(backEndFactory_, connectString_); 4927 auto_table_creator tableCreator(tc_.table_creator_1(sql)); 4928 try 4929 { 4930 rowset<row> rs1 = (sql.prepare << "select * from soci_testX"); 4931 4932 // TODO: On Linux, no exception thrown; neither from prepare, nor from execute? 4933 // soci_odbc_test_postgresql: 4934 // /home/travis/build/SOCI/soci/src/core/test/common-tests.h:3505: 4935 // void soci::tests::common_tests::test_issue67(): Assertion `!"exception expected"' failed. 4936 //FAIL("exception expected"); // relax temporarily 4937 } 4938 catch (soci_error const &e) 4939 { 4940 (void)e; 4941 } 4942 } 4943 4944 // issue 154 - Calling undefine_and_bind and then define_and_bind causes a leak. 4945 // If the test runs under memory debugger and it passes, then 4946 // soci::details::standard_use_type_backend and vector_use_type_backend must not leak 4947 TEST_CASE_METHOD(common_tests, "Bind memory leak", "[core][leak]") 4948 { 4949 soci::session sql(backEndFactory_, connectString_); 4950 auto_table_creator tableCreator(tc_.table_creator_1(sql)); 4951 sql << "insert into soci_test(id) values (1)"; 4952 { 4953 int id = 1; 4954 int val = 0; 4955 statement st(sql); 4956 st.exchange(use(id)); 4957 st.alloc(); 4958 st.prepare("select id from soci_test where id = :1"); 4959 st.define_and_bind(); 4960 st.undefine_and_bind(); 4961 st.exchange(soci::into(val)); 4962 st.define_and_bind(); 4963 st.execute(true); 4964 CHECK(val == 1); 4965 } 4966 // vector variation 4967 { 4968 std::vector<int> ids(1, 2); 4969 std::vector<int> vals(1, 1); 4970 int val = 0; 4971 statement st(sql); 4972 st.exchange(use(ids)); 4973 st.alloc(); 4974 st.prepare("insert into soci_test(id, val) values (:1, :2)"); 4975 st.define_and_bind(); 4976 st.undefine_and_bind(); 4977 st.exchange(use(vals)); 4978 st.define_and_bind(); 4979 st.execute(true); 4980 sql << "select val from soci_test where id = 2", into(val); 4981 CHECK(val == 1); 4982 } 4983 } 4984 4985 // Helper functions for issue 723 test 4986 namespace { 4987 4988 // Creates a std::tm with UK DST threshold 31st March 2019 01:00:00 4989 std::tm create_uk_dst_threshold() 4990 { 4991 std::tm dst_threshold = std::tm(); 4992 dst_threshold.tm_year = 119; // 2019 4993 dst_threshold.tm_mon = 2; // March 4994 dst_threshold.tm_mday = 31; // 31st 4995 dst_threshold.tm_hour = 1; // 1AM 4996 dst_threshold.tm_min = 0; 4997 dst_threshold.tm_sec = 0; 4998 dst_threshold.tm_isdst = -1; // Determine DST from OS 4999 return dst_threshold; 5000 } 5001 5002 // Sanity check to verify that the DST threshold causes mktime to modify 5003 // the input hour (the condition that causes issue 723). 5004 // This check really shouldn't fail but since it is the basis of the test 5005 // it is worth verifying. 5006 bool does_mktime_modify_input_hour() 5007 { 5008 std::tm dst_threshold = create_uk_dst_threshold(); 5009 std::tm verify_mktime = dst_threshold; 5010 mktime(&verify_mktime); 5011 return verify_mktime.tm_hour != dst_threshold.tm_hour; 5012 } 5013 5014 // We don't have any way to change the time zone for just this process 5015 // under MSW, so we just skip this test when not running in UK time-zone 5016 // there. Under Unix systems we can however switch to UK time zone 5017 // temporarily by just setting the TZ environment variable. 5018 #ifndef _WIN32 5019 // Helper RAII class changing time zone to the specified one in its ctor 5020 // and restoring the original time zone in its dtor. 5021 class tz_setter 5022 { 5023 public: 5024 explicit tz_setter(const std::string& time_zone) 5025 { 5026 char* tz_value = getenv("TZ"); 5027 if (tz_value != NULL) 5028 { 5029 original_tz_value_ = tz_value; 5030 } 5031 5032 setenv("TZ", time_zone.c_str(), 1 /* overwrite */); 5033 tzset(); 5034 } 5035 5036 ~tz_setter() 5037 { 5038 // Restore TZ value so other tests aren't affected. 5039 if (original_tz_value_.empty()) 5040 unsetenv("TZ"); 5041 else 5042 setenv("TZ", original_tz_value_.c_str(), 1); 5043 tzset(); 5044 } 5045 5046 private: 5047 std::string original_tz_value_; 5048 }; 5049 #endif // !_WIN32 5050 } 5051 5052 // Issue 723 - std::tm timestamp problem with DST. 5053 // When reading date/time on Daylight Saving Time threshold, hour value is 5054 // silently changed. 5055 TEST_CASE_METHOD(common_tests, "std::tm timestamp problem with DST", "[core][into][tm][dst]") 5056 { 5057 #ifdef _WIN32 5058 if (!does_mktime_modify_input_hour()) 5059 { 5060 WARN("The DST test can only be run in the UK time zone, please switch to it manually."); 5061 return; 5062 } 5063 #else // !_WIN32 5064 // Set UK timezone for this test scope. 5065 tz_setter switch_to_UK_tz("Europe/London"); 5066 5067 if (!does_mktime_modify_input_hour()) 5068 { 5069 WARN("Switching to the UK time zone unexpectedly failed, skipping the DST test."); 5070 return; 5071 } 5072 #endif // _WIN32/!_WIN32 5073 5074 // Open session and create table with a date/time column. 5075 soci::session sql(backEndFactory_, connectString_); 5076 auto_table_creator tableCreator(tc_.table_creator_1(sql)); 5077 5078 // Round trip dst threshold time to database. 5079 std::tm write_time = create_uk_dst_threshold(); 5080 sql << "insert into soci_test(tm) values(:tm)", use(write_time); 5081 std::tm read_time = std::tm(); 5082 sql << "select tm from soci_test", soci::into(read_time); 5083 5084 // Check that the round trip was consistent. 5085 std::tm dst_threshold = create_uk_dst_threshold(); 5086 CHECK(read_time.tm_year == dst_threshold.tm_year); 5087 CHECK(read_time.tm_mon == dst_threshold.tm_mon); 5088 CHECK(read_time.tm_mday == dst_threshold.tm_mday); 5089 CHECK(read_time.tm_hour == dst_threshold.tm_hour); 5090 CHECK(read_time.tm_min == dst_threshold.tm_min); 5091 CHECK(read_time.tm_sec == dst_threshold.tm_sec); 5092 } 5093 5094 TEST_CASE_METHOD(common_tests, "Insert error", "[core][insert][exception]") 5095 { 5096 soci::session sql(backEndFactory_, connectString_); 5097 5098 struct pk_table_creator : table_creator_base 5099 { 5100 explicit pk_table_creator(session& sql) : table_creator_base(sql) 5101 { 5102 // For some backends (at least Firebird), it is important to 5103 // execute the DDL statements in a separate transaction, so start 5104 // one here and commit it before using the new table below. 5105 sql.begin(); 5106 sql << "create table soci_test(" 5107 "name varchar(100) not null primary key, " 5108 "age integer not null" 5109 ")"; 5110 sql.commit(); 5111 } 5112 } table_creator(sql); 5113 5114 SECTION("literal SQL queries appear in the error message") 5115 { 5116 sql << "insert into soci_test(name, age) values ('John', 74)"; 5117 sql << "insert into soci_test(name, age) values ('Paul', 72)"; 5118 sql << "insert into soci_test(name, age) values ('George', 72)"; 5119 5120 try 5121 { 5122 // Oops, this should have been 'Ringo' 5123 sql << "insert into soci_test(name, age) values ('John', 74)"; 5124 5125 FAIL("exception expected on unique constraint violation not thrown"); 5126 } 5127 catch (soci_error const &e) 5128 { 5129 std::string const msg = e.what(); 5130 CAPTURE(msg); 5131 5132 CHECK(msg.find("John") != std::string::npos); 5133 } 5134 } 5135 5136 SECTION("SQL queries parameters appear in the error message") 5137 { 5138 char const* const names[] = { "John", "Paul", "George", "John", NULL }; 5139 int const ages[] = { 74, 72, 72, 74, 0 }; 5140 5141 std::string name; 5142 int age; 5143 5144 statement st = (sql.prepare << 5145 "insert into soci_test(name, age) values (:name, :age)", 5146 use(name), use(age)); 5147 try 5148 { 5149 int const *a = ages; 5150 for (char const* const* n = names; *n; ++n, ++a) 5151 { 5152 name = *n; 5153 age = *a; 5154 st.execute(true); 5155 } 5156 5157 FAIL("exception expected on unique constraint violation with prepared statement not thrown"); 5158 } 5159 catch (soci_error const &e) 5160 { 5161 std::string const msg = e.what(); 5162 CAPTURE(msg); 5163 5164 CHECK(msg.find("John") != std::string::npos); 5165 } 5166 } 5167 } 5168 5169 namespace 5170 { 5171 5172 // This is just a helper to avoid duplicating the same code in two sections in 5173 // the test below, it's logically part of it. 5174 void check_for_exception_on_truncation(session& sql) 5175 { 5176 // As the name column has length 20, inserting a longer string into it 5177 // shouldn't work, unless we're dealing with a database that doesn't 5178 // respect column types at all (hello SQLite). 5179 try 5180 { 5181 std::string const long_name("George Raymond Richard Martin"); 5182 sql << "insert into soci_test(name) values(:name)", use(long_name); 5183 5184 // If insert didn't throw, it should have at least preserved the data 5185 // (only SQLite does this currently). 5186 std::string name; 5187 sql << "select name from soci_test", into(name); 5188 CHECK(name == long_name); 5189 } 5190 catch (soci_error const &) 5191 { 5192 // Unfortunately the contents of the message differ too much between 5193 // the backends (most give an error about value being "too long", 5194 // Oracle says "too large" while SQL Server (via ODBC) just says that 5195 // it "would be truncated"), so we can't really check that we received 5196 // the right error here -- be optimistic and hope that we did. 5197 } 5198 } 5199 5200 // And another helper for the test below. 5201 void check_for_no_truncation(session& sql, bool with_padding) 5202 { 5203 const std::string str20 = "exactly of length 20"; 5204 5205 sql << "delete from soci_test"; 5206 5207 // Also check that there is no truncation when inserting a string of 5208 // the same length as the column size. 5209 CHECK_NOTHROW( (sql << "insert into soci_test(name) values(:s)", use(str20)) ); 5210 5211 std::string s; 5212 sql << "select name from soci_test", into(s); 5213 5214 // Firebird can pad CHAR(N) columns when using UTF-8 encoding. 5215 // the result will be padded to 80 bytes (UTF-8 max for 20 chars) 5216 if (with_padding) 5217 CHECK_EQUAL_PADDED(s, str20) 5218 else 5219 CHECK( s == str20 ); 5220 } 5221 5222 } // anonymous namespace 5223 5224 TEST_CASE_METHOD(common_tests, "Truncation error", "[core][insert][truncate][exception]") 5225 { 5226 soci::session sql(backEndFactory_, connectString_); 5227 5228 if (tc_.has_silent_truncate_bug(sql)) 5229 { 5230 WARN("Database is broken and silently truncates input data."); 5231 return; 5232 } 5233 5234 SECTION("Error given for char column") 5235 { 5236 struct fixed_name_table_creator : table_creator_base 5237 { 5238 fixed_name_table_creator(session& sql) 5239 : table_creator_base(sql) 5240 { 5241 sql << "create table soci_test(name char(20))"; 5242 } 5243 } tableCreator(sql); 5244 5245 tc_.on_after_ddl(sql); 5246 5247 check_for_exception_on_truncation(sql); 5248 5249 // Firebird can pad CHAR(N) columns when using UTF-8 encoding. 5250 check_for_no_truncation(sql, sql.get_backend_name() == "firebird"); 5251 } 5252 5253 SECTION("Error given for varchar column") 5254 { 5255 // Reuse one of the standard tables which has a varchar(20) column. 5256 auto_table_creator tableCreator(tc_.table_creator_1(sql)); 5257 5258 check_for_exception_on_truncation(sql); 5259 5260 check_for_no_truncation(sql, false); 5261 } 5262 } 5263 5264 TEST_CASE_METHOD(common_tests, "Blank padding", "[core][insert][exception]") 5265 { 5266 soci::session sql(backEndFactory_, connectString_); 5267 if (!tc_.enable_std_char_padding(sql)) 5268 { 5269 WARN("This backend doesn't pad CHAR(N) correctly, skipping test."); 5270 return; 5271 } 5272 5273 struct fixed_name_table_creator : table_creator_base 5274 { 5275 fixed_name_table_creator(session& sql) 5276 : table_creator_base(sql) 5277 { 5278 sql.begin(); 5279 sql << "create table soci_test(sc char, name char(10), name2 varchar(10))"; 5280 sql.commit(); 5281 } 5282 } tableCreator(sql); 5283 5284 std::string test1 = "abcde "; 5285 std::string singleChar = "a"; 5286 sql << "insert into soci_test(sc, name,name2) values(:sc,:name,:name2)", 5287 use(singleChar), use(test1), use(test1); 5288 5289 std::string sc, tchar,tvarchar; 5290 sql << "select sc,name,name2 from soci_test", 5291 into(sc), into(tchar), into(tvarchar); 5292 5293 // Firebird can pad "a" to "a " when using UTF-8 encoding. 5294 CHECK_EQUAL_PADDED(sc, singleChar); 5295 CHECK_EQUAL_PADDED(tchar, test1); 5296 CHECK(tvarchar == test1); 5297 5298 // Check 10-space string - same as inserting empty string since spaces will 5299 // be padded up to full size of the column. 5300 test1 = " "; 5301 singleChar = " "; 5302 sql << "update soci_test set sc=:sc, name=:name, name2=:name2", 5303 use(singleChar), use(test1), use(test1); 5304 sql << "select sc, name,name2 from soci_test", 5305 into(sc), into(tchar), into(tvarchar); 5306 5307 CHECK_EQUAL_PADDED(sc, singleChar); 5308 CHECK_EQUAL_PADDED(tchar, test1); 5309 CHECK(tvarchar == test1); 5310 } 5311 5312 TEST_CASE_METHOD(common_tests, "Select without table", "[core][select][dummy_from]") 5313 { 5314 soci::session sql(backEndFactory_, connectString_); 5315 5316 int plus17; 5317 sql << ("select abs(-17)" + sql.get_dummy_from_clause()), 5318 into(plus17); 5319 5320 CHECK(plus17 == 17); 5321 } 5322 5323 TEST_CASE_METHOD(common_tests, "String length", "[core][string][length]") 5324 { 5325 soci::session sql(backEndFactory_, connectString_); 5326 5327 auto_table_creator tableCreator(tc_.table_creator_1(sql)); 5328 5329 std::string s("123"); 5330 REQUIRE_NOTHROW(( 5331 sql << "insert into soci_test(str) values(:s)", use(s) 5332 )); 5333 5334 std::string sout; 5335 size_t slen; 5336 REQUIRE_NOTHROW(( 5337 sql << "select str," + tc_.sql_length("str") + " from soci_test", 5338 into(sout), into(slen) 5339 )); 5340 CHECK(slen == 3); 5341 CHECK(sout.length() == 3); 5342 CHECK(sout == s); 5343 5344 sql << "delete from soci_test"; 5345 5346 5347 std::vector<std::string> v; 5348 v.push_back("Hello"); 5349 v.push_back(""); 5350 v.push_back("whole of varchar(20)"); 5351 5352 REQUIRE_NOTHROW(( 5353 sql << "insert into soci_test(str) values(:s)", use(v) 5354 )); 5355 5356 std::vector<std::string> vout(10); 5357 // Although none of the strings here is really null, Oracle handles the 5358 // empty string as being null, so to avoid an error about not providing 5359 // the indicator when retrieving a null value, we must provide it here. 5360 std::vector<indicator> vind(10); 5361 std::vector<unsigned int> vlen(10); 5362 5363 REQUIRE_NOTHROW(( 5364 sql << "select str," + tc_.sql_length("str") + " from soci_test" 5365 " order by " + tc_.sql_length("str"), 5366 into(vout, vind), into(vlen) 5367 )); 5368 5369 REQUIRE(vout.size() == 3); 5370 REQUIRE(vlen.size() == 3); 5371 5372 CHECK(vlen[0] == 0); 5373 CHECK(vout[0].length() == 0); 5374 5375 CHECK(vlen[1] == 5); 5376 CHECK(vout[1].length() == 5); 5377 5378 CHECK(vlen[2] == 20); 5379 CHECK(vout[2].length() == 20); 5380 } 5381 5382 // Helper function used in some tests below. Generates an XML sample about 5383 // approximateSize bytes long. 5384 static std::string make_long_xml_string(int approximateSize = 5000) 5385 { 5386 const int tagsSize = 6 + 7; 5387 const int patternSize = 26; 5388 const int patternsCount = approximateSize / patternSize + 1; 5389 5390 std::string s; 5391 s.reserve(tagsSize + patternsCount * patternSize); 5392 5393 std::ostringstream ss; 5394 ss << "<test size=\"" << approximateSize << "\">"; 5395 for (int i = 0; i != patternsCount; ++i) 5396 { 5397 ss << "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; 5398 } 5399 ss << "</test>"; 5400 5401 return ss.str(); 5402 } 5403 5404 // The helper function to remove trailing \n from a given string. 5405 // Used for XML strings, returned from the DB. 5406 // The returned XML value doesn't need to be identical to the original one as 5407 // string, only structurally equal as XML. In particular, extra whitespace 5408 // can be added and this does happen with Oracle, for example, which adds 5409 // an extra new line, so remove it if it's present. 5410 static std::string remove_trailing_nl(std::string str) 5411 { 5412 if (!str.empty() && *str.rbegin() == '\n') 5413 { 5414 str.resize(str.length() - 1); 5415 } 5416 5417 return str; 5418 } 5419 5420 TEST_CASE_METHOD(common_tests, "CLOB", "[core][clob]") 5421 { 5422 soci::session sql(backEndFactory_, connectString_); 5423 5424 auto_table_creator tableCreator(tc_.table_creator_clob(sql)); 5425 if (!tableCreator.get()) 5426 { 5427 WARN("CLOB type not supported by the database, skipping the test."); 5428 return; 5429 } 5430 5431 long_string s1; // empty 5432 sql << "insert into soci_test(id, s) values (1, :s)", use(s1); 5433 5434 long_string s2; 5435 s2.value = "hello"; 5436 sql << "select s from soci_test where id = 1", into(s2); 5437 5438 CHECK(s2.value.size() == 0); 5439 5440 s1.value = make_long_xml_string(); 5441 5442 sql << "update soci_test set s = :s where id = 1", use(s1); 5443 5444 sql << "select s from soci_test where id = 1", into(s2); 5445 5446 CHECK(s2.value == s1.value); 5447 5448 // Check that trailing new lines are preserved. 5449 s1.value = "multi\nline\nstring\n\n"; 5450 sql << "update soci_test set s = :s where id = 1", use(s1); 5451 sql << "select s from soci_test where id = 1", into(s2); 5452 CHECK(tc_.fix_crlf_if_necessary(s2.value) == s1.value); 5453 } 5454 5455 TEST_CASE_METHOD(common_tests, "CLOB vector", "[core][clob][vector]") 5456 { 5457 soci::session sql(backEndFactory_, connectString_); 5458 5459 auto_table_creator tableCreator(tc_.table_creator_clob(sql)); 5460 if (!tableCreator.get()) 5461 { 5462 WARN("CLOB type not supported by the database, skipping the test."); 5463 return; 5464 } 5465 5466 std::vector<int> ids(2); 5467 ids[0] = 1; 5468 ids[1] = 2; 5469 std::vector<long_string> s1(2); // empty values 5470 sql << "insert into soci_test(id, s) values (:id, :s)", use(ids), use(s1); 5471 5472 std::vector<long_string> s2(2); 5473 s2[0].value = "hello_1"; 5474 s2[1].value = "hello_2"; 5475 sql << "select s from soci_test", into(s2); 5476 5477 REQUIRE(s2.size() == 2); 5478 CHECK(s2[0].value.empty()); 5479 CHECK(s2[1].value.empty()); 5480 5481 s1[0].value = make_long_xml_string(); 5482 s1[1].value = make_long_xml_string(10000); 5483 5484 sql << "update soci_test set s = :s where id = :id", use(s1), use(ids); 5485 5486 sql << "select s from soci_test", into(s2); 5487 5488 REQUIRE(s2.size() == 2); 5489 CHECK(s2[0].value == s1[0].value); 5490 CHECK(s2[1].value == s1[1].value); 5491 } 5492 5493 TEST_CASE_METHOD(common_tests, "XML", "[core][xml]") 5494 { 5495 soci::session sql(backEndFactory_, connectString_); 5496 5497 auto_table_creator tableCreator(tc_.table_creator_xml(sql)); 5498 if (!tableCreator.get()) 5499 { 5500 WARN("XML type not supported by the database, skipping the test."); 5501 return; 5502 } 5503 5504 int id = 1; 5505 xml_type xml; 5506 xml.value = make_long_xml_string(); 5507 5508 sql << "insert into soci_test (id, x) values (:1, " 5509 << tc_.to_xml(":2") 5510 << ")", 5511 use(id), use(xml); 5512 5513 xml_type xml2; 5514 5515 sql << "select " 5516 << tc_.from_xml("x") 5517 << " from soci_test where id = :1", 5518 into(xml2), use(id); 5519 5520 CHECK(xml.value == remove_trailing_nl(xml2.value)); 5521 5522 sql << "update soci_test set x = null where id = :1", use(id); 5523 5524 indicator ind; 5525 sql << "select " 5526 << tc_.from_xml("x") 5527 << " from soci_test where id = :1", 5528 into(xml2, ind), use(id); 5529 5530 CHECK(ind == i_null); 5531 5532 // Inserting malformed XML into an XML column must fail but some backends 5533 // (e.g. Firebird) don't have real XML support, so exclude them from this 5534 // test. 5535 if (tc_.has_real_xml_support()) 5536 { 5537 xml.value = "<foo></not_foo>"; 5538 CHECK_THROWS_AS( 5539 (sql << "insert into soci_test(id, x) values (2, " 5540 + tc_.to_xml(":1") + ")", 5541 use(xml) 5542 ), soci_error 5543 ); 5544 } 5545 } 5546 5547 // Tha same test as above, but using vectors of xml_type values. 5548 TEST_CASE_METHOD(common_tests, "XML vector", "[core][xml][vector]") 5549 { 5550 soci::session sql(backEndFactory_, connectString_); 5551 5552 auto_table_creator tableCreator(tc_.table_creator_xml(sql)); 5553 if (!tableCreator.get()) 5554 { 5555 WARN("XML type not supported by the database, skipping the test."); 5556 return; 5557 } 5558 5559 std::vector<int> id(2); 5560 id[0] = 1; 5561 id[1] = 1; // Use the same ID to select both objects by ID. 5562 std::vector<xml_type> xml(2); 5563 xml[0].value = make_long_xml_string(); 5564 // Check long strings handling. 5565 xml[1].value = make_long_xml_string(10000); 5566 5567 sql << "insert into soci_test (id, x) values (:1, " 5568 << tc_.to_xml(":2") 5569 << ")", 5570 use(id), use(xml); 5571 5572 std::vector<xml_type> xml2(2); 5573 5574 sql << "select " 5575 << tc_.from_xml("x") 5576 << " from soci_test where id = :1", 5577 into(xml2), use(id.at(0)); 5578 5579 CHECK(xml.at(0).value == remove_trailing_nl(xml2.at(0).value)); 5580 CHECK(xml.at(1).value == remove_trailing_nl(xml2.at(1).value)); 5581 5582 sql << "update soci_test set x = null where id = :1", use(id.at(0)); 5583 5584 std::vector<indicator> ind(2); 5585 sql << "select " 5586 << tc_.from_xml("x") 5587 << " from soci_test where id = :1", 5588 into(xml2, ind), use(id.at(0)); 5589 5590 CHECK(ind.at(0) == i_null); 5591 CHECK(ind.at(1) == i_null); 5592 } 5593 5594 TEST_CASE_METHOD(common_tests, "XML and int vectors", "[core][xml][vector]") 5595 { 5596 soci::session sql(backEndFactory_, connectString_); 5597 5598 auto_table_creator tableCreator(tc_.table_creator_xml(sql)); 5599 if (!tableCreator.get()) 5600 { 5601 WARN("XML type not supported by the database, skipping the test."); 5602 return; 5603 } 5604 5605 std::vector<int> id(3); 5606 id[0] = 0; 5607 id[1] = 1; 5608 id[2] = 2; 5609 std::vector<xml_type> xml(3); 5610 std::vector<indicator> ind(3); 5611 xml[0].value = make_long_xml_string(); 5612 ind[0] = i_ok; 5613 ind[1] = i_null; 5614 // Check long strings handling. 5615 xml[2].value = make_long_xml_string(10000); 5616 ind[2] = i_ok; 5617 5618 sql << "insert into soci_test (id, x) values (:1, " 5619 << tc_.to_xml(":2") 5620 << ")", 5621 use(id), use(xml, ind); 5622 5623 std::vector<int> id2(3); 5624 std::vector<xml_type> xml2(3); 5625 std::vector<indicator> ind2(3); 5626 5627 sql << "select id, " 5628 << tc_.from_xml("x") 5629 << " from soci_test order by id", 5630 into(id2), into(xml2, ind2); 5631 5632 CHECK(id.at(0) == id2.at(0)); 5633 CHECK(id.at(1) == id2.at(1)); 5634 CHECK(id.at(2) == id2.at(2)); 5635 5636 CHECK(xml.at(0).value == remove_trailing_nl(xml2.at(0).value)); 5637 CHECK(xml.at(2).value == remove_trailing_nl(xml2.at(2).value)); 5638 5639 CHECK(ind.at(0) == ind2.at(0)); 5640 CHECK(ind.at(1) == ind2.at(1)); 5641 CHECK(ind.at(2) == ind2.at(2)); 5642 } 5643 5644 TEST_CASE_METHOD(common_tests, "Into XML vector with several fetches", "[core][xml][into][vector][statement]") 5645 { 5646 int stringSize = 0; 5647 SECTION("short string") 5648 { 5649 stringSize = 100; 5650 } 5651 SECTION("long string") 5652 { 5653 stringSize = 10000; 5654 } 5655 5656 // Skip the rest when not executing the current section. 5657 if (!stringSize) 5658 return; 5659 5660 soci::session sql(backEndFactory_, connectString_); 5661 5662 auto_table_creator tableCreator(tc_.table_creator_xml(sql)); 5663 if (!tableCreator.get()) 5664 { 5665 WARN("XML type not supported by the database, skipping the test."); 5666 return; 5667 } 5668 5669 int const count = 5; 5670 std::vector<xml_type> values(count); 5671 for (int i = 0; i != count; ++i) 5672 values[i].value = make_long_xml_string(stringSize + i*100); 5673 5674 sql << "insert into soci_test (x) values (" 5675 << tc_.to_xml(":2") 5676 << ")", 5677 use(values); 5678 5679 std::vector<xml_type> result(3); 5680 soci::statement st = (sql.prepare << 5681 "select " << tc_.from_xml("x") << " from soci_test", into(result)); 5682 5683 st.execute(true); 5684 REQUIRE(result.size() == 3); 5685 CHECK(remove_trailing_nl(result[0].value) == values[0].value); 5686 CHECK(remove_trailing_nl(result[1].value) == values[1].value); 5687 CHECK(remove_trailing_nl(result[2].value) == values[2].value); 5688 5689 REQUIRE(st.fetch()); 5690 REQUIRE(result.size() == 2); 5691 CHECK(remove_trailing_nl(result[0].value) == values[3].value); 5692 CHECK(remove_trailing_nl(result[1].value) == values[4].value); 5693 5694 REQUIRE(!st.fetch()); 5695 } 5696 5697 TEST_CASE_METHOD(common_tests, "Logger", "[core][log]") 5698 { 5699 // Logger class used for testing: appends all queries to the provided 5700 // buffer. 5701 class test_log_impl : public soci::logger_impl 5702 { 5703 public: 5704 explicit test_log_impl(std::vector<std::string>& logbuf) 5705 : m_logbuf(logbuf) 5706 { 5707 } 5708 5709 virtual void start_query(std::string const & query) 5710 { 5711 m_logbuf.push_back(query); 5712 } 5713 5714 private: 5715 virtual logger_impl* do_clone() const 5716 { 5717 return new test_log_impl(m_logbuf); 5718 } 5719 5720 std::vector<std::string>& m_logbuf; 5721 }; 5722 5723 soci::session sql(backEndFactory_, connectString_); 5724 auto_table_creator tableCreator(tc_.table_creator_1(sql)); 5725 5726 soci::logger const logger_orig = sql.get_logger(); 5727 5728 std::vector<std::string> logbuf; 5729 sql.set_logger(new test_log_impl(logbuf)); 5730 5731 int count; 5732 sql << "select count(*) from soci_test", into(count); 5733 5734 REQUIRE( logbuf.size() == 1 ); 5735 CHECK( logbuf.front() == "select count(*) from soci_test" ); 5736 5737 sql.set_logger(logger_orig); 5738 } 5739 5740 // These tests are disabled by default, as they require manual intevention, but 5741 // can be run by explicitly giving their names on the command line. 5742 5743 // Check if reconnecting to the database after losing connection to it works. 5744 TEST_CASE_METHOD(common_tests, "Reconnect", "[keep-alive][.]") 5745 { 5746 soci::session sql(backEndFactory_, connectString_); 5747 auto_table_creator tableCreator(tc_.table_creator_1(sql)); 5748 5749 int id = 17; 5750 sql << "insert into soci_test (id) values (:id)", use(id); 5751 5752 REQUIRE_NOTHROW( sql.commit() ); 5753 CHECK( sql.is_connected() ); 5754 5755 std::cout << "Please break connection to the database " 5756 "(stop the server, unplug the network cable, ...) " 5757 "and press Enter" << std::endl; 5758 std::cin.get(); 5759 5760 try 5761 { 5762 CHECK( !sql.is_connected() ); 5763 5764 int id2; 5765 sql << "select id from soci_test", into(id2); 5766 5767 FAIL("Connection to the database still available"); 5768 return; 5769 } 5770 catch (soci_error const& e) 5771 { 5772 if ( sql.get_backend_name() == "odbc" || 5773 e.get_error_category() == soci_error::unknown ) 5774 { 5775 WARN( "Skipping error check because ODBC driver returned " 5776 "unknown error: " << e.what() ); 5777 } 5778 else 5779 { 5780 INFO( "Exception message: " << e.what() ); 5781 CHECK( e.get_error_category() == soci_error::connection_error ); 5782 } 5783 } 5784 5785 std::cout << "Please undo the previous action " 5786 "(restart the server, plug the cable back, ...) " 5787 "and press Enter" << std::endl; 5788 std::cin.get(); 5789 5790 REQUIRE_NOTHROW( sql.reconnect() ); 5791 CHECK( sql.is_connected() ); 5792 5793 int id2 = 1234; 5794 sql << "select id from soci_test", into(id2); 5795 CHECK( id2 == id ); 5796 } 5797 5798 // Check if automatically reconnecting to the database works. 5799 // 5800 // Note: this test doesn't work at all, failover doesn't happen neither with 5801 // Oracle nor with PostgreSQL (which are the only backends for which it's 5802 // implemented at all) and it's not clear how is it even supposed to work. 5803 TEST_CASE_METHOD(common_tests, "Failover", "[keep-alive][.]") 5804 { 5805 soci::session sql(backEndFactory_, connectString_); 5806 5807 class MyCallback : public soci::failover_callback 5808 { 5809 public: 5810 MyCallback() : attempted_(false), reconnected_(false) 5811 { 5812 } 5813 5814 bool did_reconnect() const { return reconnected_; } 5815 5816 void started() override 5817 { 5818 std::cout << "Please undo the previous action " 5819 "(restart the server, plug the cable back, ...) " 5820 "and press Enter" << std::endl; 5821 std::cin.get(); 5822 } 5823 5824 void failed(bool& retry, std::string&) override 5825 { 5826 // We only retry once. 5827 retry = !attempted_; 5828 attempted_ = true; 5829 } 5830 5831 void finished(soci::session&) override 5832 { 5833 reconnected_ = true; 5834 } 5835 5836 void aborted() override 5837 { 5838 FAIL( "Failover aborted" ); 5839 } 5840 5841 private: 5842 bool attempted_; 5843 bool reconnected_; 5844 } myCallback; 5845 5846 sql.set_failover_callback(myCallback); 5847 5848 auto_table_creator tableCreator(tc_.table_creator_1(sql)); 5849 5850 int id = 17; 5851 sql << "insert into soci_test (id) values (:id)", use(id); 5852 REQUIRE_NOTHROW( sql.commit() ); 5853 5854 std::cout << "Please break connection to the database " 5855 "(stop the server, unplug the network cable, ...) " 5856 "and press Enter" << std::endl; 5857 std::cin.get(); 5858 5859 int id2; 5860 sql << "select id from soci_test", into(id2); 5861 CHECK( id2 == id ); 5862 5863 CHECK( myCallback.did_reconnect() ); 5864 } 5865 5866 } // namespace test_cases 5867 5868 } // namespace tests 5869 5870 } // namespace soci 5871 5872 #endif // SOCI_COMMON_TESTS_H_INCLUDED