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