common-tests.h 131 KB
Newer Older
1
//
msobczak's avatar
msobczak committed
2
// Copyright (C) 2004-2008 Maciej Sobczak, Stephen Hutton
3 4 5 6 7
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//

msobczak's avatar
msobczak committed
8 9
#ifndef SOCI_COMMON_TESTS_H_INCLUDED
#define SOCI_COMMON_TESTS_H_INCLUDED
10

11
#include "soci/soci.h"
12

13
#ifdef SOCI_HAVE_BOOST
14
// explicitly pull conversions for Boost's optional, tuple and fusion:
15
#include <boost/version.hpp>
16 17 18
#include "soci/boost-optional.h"
#include "soci/boost-tuple.h"
#include "soci/boost-gregorian-date.h"
19
#if defined(BOOST_VERSION) && BOOST_VERSION >= 103500
20
#include "soci/boost-fusion.h"
21
#endif // BOOST_VERSION
22
#endif // SOCI_HAVE_BOOST
23

24 25
#include "soci-compiler.h"

26 27 28
#define CATCH_CONFIG_RUNNER
#include <catch.hpp>

29 30 31 32 33
#if defined(_MSC_VER) && (_MSC_VER < 1500)
#undef SECTION
#define SECTION(name) INTERNAL_CATCH_SECTION(name, "dummy-for-vc8")
#endif

34
#include <algorithm>
35
#include <cassert>
36 37
#include <clocale>
#include <cstdlib>
38
#include <cmath>
39
#include <iomanip>
40
#include <iostream>
41
#include <limits>
42
#include <string>
43
#include <typeinfo>
44

45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
// Although SQL standard mandates right padding CHAR(N) values to their length
// with spaces, some backends don't confirm to it:
//
//  - Firebird does pad the string but to the byte-size (not character size) of
//  the column (i.e. CHAR(10) NONE is padded to 10 bytes but CHAR(10) UTF8 --
//  to 40).
//  - For MySql PAD_CHAR_TO_FULL_LENGTH option must be set, otherwise the value
//  is trimmed.
//  - SQLite never behaves correctly at all.
//
// This method will check result string from column defined as fixed char It
// will check only bytes up to the original string size. If padded string is
// bigger than expected string then all remaining chars must be spaces so if
// any non-space character is found it will fail.
void
checkEqualPadded(const std::string& padded_str, const std::string& expected_str)
{
    size_t const len = expected_str.length();
    std::string const start_str(padded_str, 0, len);

    if (start_str != expected_str)
    {
        throw soci::soci_error(
                "Expected string \"" + expected_str + "\" "
                "is different from the padded string \"" + padded_str + "\""
              );
    }

    if (padded_str.length() > len)
    {
        std::string const end_str(padded_str, len);
        if (end_str != std::string(padded_str.length() - len, ' '))
        {
            throw soci::soci_error(
                  "\"" + padded_str + "\" starts with \"" + padded_str +
                  "\" but non-space characater(s) are found aftewards"
                );
        }
    }
}

#define CHECK_EQUAL_PADDED(padded_str, expected_str) \
    CHECK_NOTHROW(checkEqualPadded(padded_str, expected_str));

89 90 91 92 93 94 95
// Objects used later in tests 14,15
struct PhonebookEntry
{
    std::string name;
    std::string phone;
};

msobczak's avatar
msobczak committed
96
struct PhonebookEntry2 : public PhonebookEntry
97 98 99
{
};

100 101 102 103 104
class PhonebookEntry3
{
public:
    void setName(std::string const & n) { name_ = n; }
    std::string getName() const { return name_; }
msobczak's avatar
msobczak committed
105

106 107 108 109 110 111 112 113
    void setPhone(std::string const & p) { phone_ = p; }
    std::string getPhone() const { return phone_; }

public:
    std::string name_;
    std::string phone_;
};

114
// user-defined object for test26 and test28
msobczak's avatar
msobczak committed
115 116 117
class MyInt
{
public:
Sergei Nikulov's avatar
Sergei Nikulov committed
118
    MyInt() : i_() {}
msobczak's avatar
msobczak committed
119 120 121 122 123 124 125
    MyInt(int i) : i_(i) {}
    void set(int i) { i_ = i; }
    int get() const { return i_; }
private:
    int i_;
};

126
namespace soci
127
{
msobczak's avatar
msobczak committed
128 129 130 131 132 133

// basic type conversion for user-defined type with single base value
template<> struct type_conversion<MyInt>
{
    typedef int base_type;

134
    static void from_base(int i, indicator ind, MyInt &mi)
msobczak's avatar
msobczak committed
135
    {
136
        if (ind == i_ok)
msobczak's avatar
msobczak committed
137 138 139 140 141
        {
            mi.set(i);
        }
    }

142
    static void to_base(MyInt const &mi, int &i, indicator &ind)
msobczak's avatar
msobczak committed
143 144
    {
        i = mi.get();
145
        ind = i_ok;
msobczak's avatar
msobczak committed
146
    }
msobczak's avatar
msobczak committed
147
};
msobczak's avatar
msobczak committed
148 149

// basic type conversion on many values (ORM)
150
template<> struct type_conversion<PhonebookEntry>
151
{
152
    typedef soci::values base_type;
153

154
    static void from_base(values const &v, indicator /* ind */, PhonebookEntry &pe)
155
    {
156
        // here we ignore the possibility the the whole object might be NULL
157 158
        pe.name = v.get<std::string>("NAME");
        pe.phone = v.get<std::string>("PHONE", "<NULL>");
159
    }
160

161
    static void to_base(PhonebookEntry const &pe, values &v, indicator &ind)
162
    {
163
        v.set("NAME", pe.name);
164 165
        v.set("PHONE", pe.phone, pe.phone.empty() ? i_null : i_ok);
        ind = i_ok;
166
    }
msobczak's avatar
msobczak committed
167
};
168

169
// type conversion which directly calls values::get_indicator()
170
template<> struct type_conversion<PhonebookEntry2>
171
{
172
    typedef soci::values base_type;
173

174
    static void from_base(values const &v, indicator /* ind */, PhonebookEntry2 &pe)
175
    {
176 177
        // here we ignore the possibility the the whole object might be NULL

178
        pe.name = v.get<std::string>("NAME");
179 180
        indicator ind = v.get_indicator("PHONE"); //another way to test for null
        pe.phone = ind == i_null ? "<NULL>" : v.get<std::string>("PHONE");
181
    }
182

183
    static void to_base(PhonebookEntry2 const &pe, values &v, indicator &ind)
184
    {
185
        v.set("NAME", pe.name);
186 187
        v.set("PHONE", pe.phone, pe.phone.empty() ? i_null : i_ok);
        ind = i_ok;
188
    }
msobczak's avatar
msobczak committed
189
};
190

191 192 193 194
template<> struct type_conversion<PhonebookEntry3>
{
    typedef soci::values base_type;

195
    static void from_base(values const &v, indicator /* ind */, PhonebookEntry3 &pe)
196 197 198
    {
        // here we ignore the possibility the the whole object might be NULL

199 200
        pe.setName(v.get<std::string>("NAME"));
        pe.setPhone(v.get<std::string>("PHONE", "<NULL>"));
201 202
    }

203
    static void to_base(PhonebookEntry3 const &pe, values &v, indicator &ind)
204
    {
205
        v.set("NAME", pe.getName());
206 207
        v.set("PHONE", pe.getPhone(), pe.getPhone().empty() ? i_null : i_ok);
        ind = i_ok;
208 209 210
    }
};

msobczak's avatar
msobczak committed
211
} // namespace soci
212

213
namespace soci
214 215 216 217
{
namespace tests
{

218
// TODO: improve cleanup capabilities by subtypes, soci_test name may be omitted --mloskot
219
//       i.e. optional ctor param accepting custom table name
220
class table_creator_base
221 222
{
public:
223 224
    table_creator_base(session& sql)
        : msession(sql) { drop(); }
225

226
    virtual ~table_creator_base() { drop();}
227 228 229
private:
    void drop()
    {
230 231 232 233 234 235 236 237 238
        try
        {
            msession << "drop table soci_test";
        }
        catch (soci_error const& e)
        {
            //std::cerr << e.what() << std::endl;
            e.what();
        }
239
    }
240
    session& msession;
241 242

    SOCI_NOT_COPYABLE(table_creator_base)
243 244
};

245
class procedure_creator_base
246 247
{
public:
248 249
    procedure_creator_base(session& sql)
        : msession(sql) { drop(); }
250

251
    virtual ~procedure_creator_base() { drop();}
252 253 254
private:
    void drop()
    {
msobczak's avatar
msobczak committed
255
        try { msession << "drop procedure soci_test"; } catch (soci_error&) {}
256
    }
257
    session& msession;
258 259

    SOCI_NOT_COPYABLE(procedure_creator_base)
260 261
};

262
class function_creator_base
263 264
{
public:
265 266
    function_creator_base(session& sql)
        : msession(sql) { drop(); }
267

268
    virtual ~function_creator_base() { drop();}
269 270

protected:
271
    virtual std::string dropstatement()
msobczak's avatar
msobczak committed
272
    {
273 274 275
        return "drop function soci_test";
    }

276 277 278
private:
    void drop()
    {
msobczak's avatar
msobczak committed
279
        try { msession << dropstatement(); } catch (soci_error&) {}
280
    }
281
    session& msession;
282 283

    SOCI_NOT_COPYABLE(function_creator_base)
284 285
};

286 287
// This is a singleton class, at any given time there is at most one test
// context alive and common_tests fixture class uses it.
288
class test_context_base
289 290
{
public:
291
    test_context_base(backend_factory const &backEnd,
292 293
                    std::string const &connectString)
        : backEndFactory_(backEnd),
294 295
          connectString_(connectString)
    {
296 297 298 299 300 301
        // This can't be a CHECK() because the test context is constructed
        // outside of any test.
        assert(!the_test_context_);

        the_test_context_ = this;

302 303 304 305
        // To allow running tests in non-default ("C") locale, the following
        // environment variable can be set and then the current default locale
        // (which can itself be changed by setting LC_ALL environment variable)
        // will then be used.
306
        if (std::getenv("SOCI_TEST_USE_LC_ALL"))
307 308
            std::setlocale(LC_ALL, "");
    }
309

310 311
    static test_context_base const& get_instance()
    {
312
        REQUIRE(the_test_context_);
313 314 315 316

        return *the_test_context_;
    }

317
    backend_factory const & get_backend_factory() const
318 319 320
    {
        return backEndFactory_;
    }
321

322
    std::string get_connect_string() const
323 324 325 326
    {
        return connectString_;
    }

327
    virtual std::string to_date_time(std::string const &dateTime) const = 0;
328

329 330 331
    virtual table_creator_base* table_creator_1(session&) const = 0;
    virtual table_creator_base* table_creator_2(session&) const = 0;
    virtual table_creator_base* table_creator_3(session&) const = 0;
332
    virtual table_creator_base* table_creator_4(session&) const = 0;
333

334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352
    // Override this to return the table creator for a simple table containing
    // an integer "id" column and CLOB "s" one.
    //
    // Returns null by default to indicate that CLOB is not supported.
    virtual table_creator_base* table_creator_clob(session&) const { return NULL; }

    // Override this to return the table creator for a simple table containing
    // an integer "id" column and XML "x" one.
    //
    // Returns null by default to indicate that XML is not supported.
    virtual table_creator_base* table_creator_xml(session&) const { return NULL; }

    // Return the casts that must be used to convert the between the database
    // XML type and the query parameters.
    //
    // By default no special casts are done.
    virtual std::string to_xml(std::string const& x) const { return x; }
    virtual std::string from_xml(std::string const& x) const { return x; }

353 354 355 356 357 358 359
    // Override this if the backend not only supports working with XML values
    // (and so returns a non-null value from table_creator_xml()), but the
    // database itself has real XML support instead of just allowing to store
    // and retrieve XML as text. "Real" support means at least preventing the
    // application from storing malformed XML in the database.
    virtual bool has_real_xml_support() const { return false; }

360 361 362 363
    // Override this if the backend doesn't handle floating point values
    // correctly, i.e. writing a value and reading it back doesn't return
    // *exactly* the same value.
    virtual bool has_fp_bug() const { return false; }
364

365 366 367 368 369
    // Override this if the backend doesn't handle multiple active select
    // statements at the same time, i.e. a result set must be entirely consumed
    // before creating a new one (this is the case of MS SQL without MARS).
    virtual bool has_multiple_select_bug() const { return false; }

370
    // Override this if the backend may not have transactions support.
371
    virtual bool has_transactions_support(session&) const { return true; }
372

373 374 375 376 377 378 379 380
    // Override this if the backend silently truncates string values too long
    // to fit by default.
    virtual bool has_silent_truncate_bug(session&) const { return false; }

    // Override this to call commit() if it's necessary for the DDL statements
    // to be taken into account (currently this is only the case for Firebird).
    virtual void on_after_ddl(session&) const { }

381 382 383 384 385
    // Put the database in SQL-complient mode for CHAR(N) values, return false
    // if it's impossible, i.e. if the database doesn't behave correctly
    // whatever we do.
    virtual bool enable_std_char_padding(session&) const { return true; }

386 387 388 389
    // Return the SQL expression giving the length of the specified string,
    // i.e. "char_length(s)" in standard SQL but often "len(s)" or "length(s)"
    // in practice and sometimes even worse (thanks Oracle).
    virtual std::string sql_length(std::string const& s) const = 0;
390

391 392 393 394
    virtual ~test_context_base()
    {
        the_test_context_ = NULL;
    }
395 396

private:
397
    backend_factory const &backEndFactory_;
398
    std::string const connectString_;
399 400

    static test_context_base* the_test_context_;
401 402

    SOCI_NOT_COPYABLE(test_context_base)
403 404
};

405 406 407 408 409
// Currently all tests consist of just a single source file, so we can define
// this member here because this header is included exactly once.
tests::test_context_base* tests::test_context_base::the_test_context_ = NULL;


410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436
// Compare doubles for approximate equality. This has to be used everywhere
// where we write "3.14" (or "6.28") to the database as a string and then
// compare the value read back with the literal 3.14 floating point constant
// because they are not the same.
//
// It is also used for the backends which currently don't handle doubles
// correctly.
//
// Notice that this function is normally not used directly but rather from the
// macro below.
inline bool are_doubles_approx_equal(double const a, double const b)
{
    // The formula taken from CATCH test framework
    // https://github.com/philsquared/Catch/
    // Thanks to Richard Harris for his help refining this formula
    double const epsilon(std::numeric_limits<float>::epsilon() * 100);
    double const scale(1.0);
    return std::fabs(a - b) < epsilon * (scale + (std::max)(std::fabs(a), std::fabs(b)));
}

// This is a macro to ensure we use the correct line numbers. The weird
// do/while construction is used to make this a statement and the even weirder
// condition in while ensures that the loop is executed exactly once without
// triggering warnings from MSVC about the condition being always false.
#define ASSERT_EQUAL_APPROX(a, b) \
    do { \
      if (!are_doubles_approx_equal((a), (b))) { \
437
        FAIL( "Approximate equality check failed: " \
438 439
                  << std::fixed \
                  << std::setprecision(std::numeric_limits<double>::digits10 + 1) \
440
                  << (a) << " != " << (b) ); \
441 442 443 444
      } \
    } while ( (void)0, 0 )


445 446
// Exact double comparison function. We need one, instead of writing "a == b",
// only in order to have some place to put the pragmas disabling gcc warnings.
447
inline bool
448
are_doubles_exactly_equal(double a, double b)
449 450 451 452 453 454 455
{
    // Avoid g++ warnings: we do really want the exact equality here.
    GCC_WARNING_SUPPRESS(float-equal)

    return a == b;

    GCC_WARNING_RESTORE(float-equal)
456 457 458 459 460
}

#define ASSERT_EQUAL_EXACT(a, b) \
    do { \
      if (!are_doubles_exactly_equal((a), (b))) { \
461
        FAIL( "Exact equality check failed: " \
462 463
                  << std::fixed \
                  << std::setprecision(std::numeric_limits<double>::digits10 + 1) \
464
                  << (a) << " != " << (b) ); \
465 466 467 468 469 470 471 472 473 474 475 476
      } \
    } while ( (void)0, 0 )


// Compare two floating point numbers either exactly or approximately depending
// on test_context::has_fp_bug() return value.
inline bool
are_doubles_equal(test_context_base const& tc, double a, double b)
{
    return tc.has_fp_bug()
                ? are_doubles_approx_equal(a, b)
                : are_doubles_exactly_equal(a, b);
477 478 479 480 481 482 483 484 485 486 487 488 489 490
}

// This macro should be used when where we don't have any problems with string
// literals vs floating point literals mismatches described above and would
// ideally compare the numbers exactly but, unfortunately, currently can't do
// this unconditionally because at least some backends are currently buggy and
// don't handle the floating point values correctly.
//
// This can be only used from inside the common_tests class as it relies on
// having an accessible "tc_" variable to determine whether exact or
// approximate comparison should be used.
#define ASSERT_EQUAL(a, b) \
    do { \
      if (!are_doubles_equal(tc_, (a), (b))) { \
491
        FAIL( "Equality check failed: " \
492 493
                  << std::fixed \
                  << std::setprecision(std::numeric_limits<double>::digits10 + 1) \
494
                  << (a) << " != " << (b) ); \
495 496 497 498
      } \
    } while ( (void)0, 0 )


499
class common_tests
500 501
{
public:
502 503 504 505
    common_tests()
    : tc_(test_context_base::get_instance()),
      backEndFactory_(tc_.get_backend_factory()),
      connectString_(tc_.get_connect_string())
506 507
    {}

508
protected:
509 510
    test_context_base const & tc_;
    backend_factory const &backEndFactory_;
511
    std::string const connectString_;
512 513

    SOCI_NOT_COPYABLE(common_tests)
514
};
515

516
typedef cxx_details::auto_ptr<table_creator_base> auto_table_creator;
517

518 519 520 521 522 523 524 525
// Define the test cases in their own namespace to avoid clashes with the test
// cases defined in individual backend tests: as only line number is used for
// building the name of the "anonymous" function by the TEST_CASE macro, we
// could have a conflict between a test defined here and in some backend if
// they happened to start on the same line.
namespace test_cases
{

526
TEST_CASE_METHOD(common_tests, "Exception on not connected", "[core][exception]")
527
{
528 529 530 531 532 533 534 535 536 537 538 539 540 541 542
    soci::session sql; // no connection

    // ensure connection is checked, no crash occurs
    CHECK_THROWS_AS(sql.begin(), soci_error);
    CHECK_THROWS_AS(sql.commit(), soci_error);
    CHECK_THROWS_AS(sql.rollback(), soci_error);
    CHECK_THROWS_AS(sql.get_backend_name(), soci_error);
    CHECK_THROWS_AS(sql.make_statement_backend(), soci_error);
    CHECK_THROWS_AS(sql.make_rowid_backend(), soci_error);
    CHECK_THROWS_AS(sql.make_blob_backend(), soci_error);

    std::string s;
    long l;
    CHECK_THROWS_AS(sql.get_next_sequence_value(s, l), soci_error);
    CHECK_THROWS_AS(sql.get_last_insert_id(s, l), soci_error);
543
}
544

545
TEST_CASE_METHOD(common_tests, "Basic functionality", "[core][basics]")
546
{
547
    soci::session sql(backEndFactory_, connectString_);
548

549
    auto_table_creator tableCreator(tc_.table_creator_1(sql));
550

551
    CHECK_THROWS_AS(sql << "drop table soci_test_nosuchtable", soci_error);
552 553 554 555

    sql << "insert into soci_test (id) values (" << 123 << ")";
    int id;
    sql << "select id from soci_test", into(id);
556
    CHECK(id == 123);
557 558 559 560 561 562 563 564 565 566 567 568

    sql << "insert into soci_test (id) values (" << 234 << ")";
    sql << "insert into soci_test (id) values (" << 345 << ")";
    // Test prepare, execute, fetch correctness
    statement st = (sql.prepare << "select id from soci_test", into(id));
    st.execute();
    int count = 0;
    while(st.fetch())
        count++;
    CHECK(count == 3 );
    bool fetchEnd = st.fetch(); // All the data has been read here so additional fetch must return false
    CHECK(fetchEnd == false);
569 570 571
}

// "into" tests, type conversions, etc.
572
TEST_CASE_METHOD(common_tests, "Use and into", "[core][into]")
573
{
574
    soci::session sql(backEndFactory_, connectString_);
575

576
    auto_table_creator tableCreator(tc_.table_creator_1(sql));
577

578 579
    SECTION("Round trip works for char")
    {
580 581 582 583 584
        char c('a');
        sql << "insert into soci_test(c) values(:c)", use(c);
        sql << "select c from soci_test", into(c);
        CHECK(c == 'a');
    }
585

586
    SECTION("Round trip works for string")
587 588 589 590 591 592 593
    {
        std::string helloSOCI("Hello, SOCI!");
        sql << "insert into soci_test(str) values(:s)", use(helloSOCI);
        std::string str;
        sql << "select str from soci_test", into(str);
        CHECK(str == "Hello, SOCI!");
    }
msobczak's avatar
msobczak committed
594

595
    SECTION("Round trip works for short")
596 597 598 599 600 601 602
    {
        short three(3);
        sql << "insert into soci_test(sh) values(:id)", use(three);
        short sh(0);
        sql << "select sh from soci_test", into(sh);
        CHECK(sh == 3);
    }
603

604
    SECTION("Round trip works for int")
605 606 607 608 609 610 611
    {
        int five(5);
        sql << "insert into soci_test(id) values(:id)", use(five);
        int i(0);
        sql << "select id from soci_test", into(i);
        CHECK(i == 5);
    }
612

613
    SECTION("Round trip works for unsigned long")
614 615 616 617 618 619 620
    {
        unsigned long seven(7);
        sql << "insert into soci_test(ul) values(:ul)", use(seven);
        unsigned long ul(0);
        sql << "select ul from soci_test", into(ul);
        CHECK(ul == 7);
    }
621

622
    SECTION("Round trip works for double")
623 624 625 626 627 628 629
    {
        double pi(3.14159265);
        sql << "insert into soci_test(d) values(:d)", use(pi);
        double d(0.0);
        sql << "select d from soci_test", into(d);
        ASSERT_EQUAL(d, pi);
    }
630

631
    SECTION("Round trip works for date without time")
632
    {
633
        std::tm nov15 = std::tm();
634 635 636 637 638 639
        nov15.tm_year = 105;
        nov15.tm_mon = 10;
        nov15.tm_mday = 15;
        nov15.tm_hour = 0;
        nov15.tm_min = 0;
        nov15.tm_sec = 0;
640

641
        sql << "insert into soci_test(tm) values(:tm)", use(nov15);
642

643
        std::tm t = std::tm();
644 645 646 647 648 649 650 651
        sql << "select tm from soci_test", into(t);
        CHECK(t.tm_year == 105);
        CHECK(t.tm_mon  == 10);
        CHECK(t.tm_mday == 15);
        CHECK(t.tm_hour == 0);
        CHECK(t.tm_min  == 0);
        CHECK(t.tm_sec  == 0);
    }
652

653
    SECTION("Round trip works for date with time")
654
    {
655
        std::tm nov15 = std::tm();
656 657 658 659 660 661
        nov15.tm_year = 105;
        nov15.tm_mon = 10;
        nov15.tm_mday = 15;
        nov15.tm_hour = 22;
        nov15.tm_min = 14;
        nov15.tm_sec = 17;
662

663
        sql << "insert into soci_test(tm) values(:tm)", use(nov15);
664

665
        std::tm t = std::tm();
666 667 668 669 670 671 672 673
        sql << "select tm from soci_test", into(t);
        CHECK(t.tm_year == 105);
        CHECK(t.tm_mon  == 10);
        CHECK(t.tm_mday == 15);
        CHECK(t.tm_hour == 22);
        CHECK(t.tm_min  == 14);
        CHECK(t.tm_sec  == 17);
    }
674

675
    SECTION("Indicator is filled correctly in the simplest case")
676 677 678 679 680
    {
        int id(1);
        std::string str("Hello");
        sql << "insert into soci_test(id, str) values(:id, :str)",
            use(id), use(str);
681

682 683 684 685 686
        int i;
        indicator ind;
        sql << "select id from soci_test", into(i, ind);
        CHECK(ind == i_ok);
    }
687

688
    SECTION("Indicators work correctly more generally")
689 690 691 692 693 694
    {
        sql << "insert into soci_test(id,tm) values(NULL,NULL)";
        int i;
        indicator ind;
        sql << "select id from soci_test", into(i, ind);
        CHECK(ind == i_null);
695

696
        // additional test for NULL with std::tm
697
        std::tm t = std::tm();
698 699
        sql << "select tm from soci_test", into(t, ind);
        CHECK(ind == i_null);
700

701 702 703 704 705 706 707 708
        try
        {
            // expect error
            sql << "select id from soci_test", into(i);
            FAIL("expected exception not thrown");
        }
        catch (soci_error const &e)
        {
709
            CHECK(e.get_error_message() ==
710
                "Null value fetched and no indicator defined.");
711 712
        }

713 714 715 716 717 718 719 720 721 722 723 724
        sql << "select id from soci_test where id = 1000", into(i, ind);
        CHECK(sql.got_data() == false);

        // no data expected
        sql << "select id from soci_test where id = 1000", into(i);
        CHECK(sql.got_data() == false);

        // no data expected, test correct behaviour with use
        int id = 1000;
        sql << "select id from soci_test where id = :id", use(id), into(i);
        CHECK(sql.got_data() == false);
    }
725 726 727
}

// repeated fetch and bulk fetch
728
TEST_CASE_METHOD(common_tests, "Repeated and bulk fetch", "[core][bulk]")
729
{
730
    soci::session sql(backEndFactory_, connectString_);
731

732 733
    // create and populate the test table
    auto_table_creator tableCreator(tc_.table_creator_1(sql));
734

735 736
    SECTION("char")
    {
737 738
        char c;
        for (c = 'a'; c <= 'z'; ++c)
739
        {
740 741
            sql << "insert into soci_test(c) values(\'" << c << "\')";
        }
742

743 744 745
        int count;
        sql << "select count(*) from soci_test", into(count);
        CHECK(count == 'z' - 'a' + 1);
746

747 748
        {
            char c2 = 'a';
749

750 751
            statement st = (sql.prepare <<
                "select c from soci_test order by c", into(c));
752

753 754 755 756 757 758 759 760 761 762
            st.execute();
            while (st.fetch())
            {
                CHECK(c == c2);
                ++c2;
            }
            CHECK(c2 == 'a' + count);
        }
        {
            char c2 = 'a';
763

764 765 766 767 768 769 770
            std::vector<char> vec(10);
            statement st = (sql.prepare <<
                "select c from soci_test order by c", into(vec));
            st.execute();
            while (st.fetch())
            {
                for (std::size_t i = 0; i != vec.size(); ++i)
771
                {
772
                    CHECK(c2 == vec[i]);
773 774 775
                    ++c2;
                }

776
                vec.resize(10);
777
            }
778 779
            CHECK(c2 == 'a' + count);
        }
780

781 782 783 784
        {
            // verify an exception is thrown when empty vector is used
            std::vector<char> vec;
            try
785
            {
786 787 788 789 790
                sql << "select c from soci_test", into(vec);
                FAIL("expected exception not thrown");
            }
            catch (soci_error const &e)
            {
791 792
                 CHECK(e.get_error_message() ==
                     "Vectors of size 0 are not allowed.");
793
            }
794 795 796 797 798
        }

    }

    // repeated fetch and bulk fetch of std::string
799
    SECTION("std::string")
800 801 802 803 804 805 806 807 808
    {
        int const rowsToTest = 10;
        for (int i = 0; i != rowsToTest; ++i)
        {
            std::ostringstream ss;
            ss << "Hello_" << i;

            sql << "insert into soci_test(str) values(\'"
                << ss.str() << "\')";
809 810
        }

811 812 813 814
        int count;
        sql << "select count(*) from soci_test", into(count);
        CHECK(count == rowsToTest);

815
        {
816 817 818 819 820 821 822
            int i = 0;
            std::string s;
            statement st = (sql.prepare <<
                "select str from soci_test order by str", into(s));

            st.execute();
            while (st.fetch())
823 824 825
            {
                std::ostringstream ss;
                ss << "Hello_" << i;
826 827
                CHECK(s == ss.str());
                ++i;
828
            }
829 830 831 832
            CHECK(i == rowsToTest);
        }
        {
            int i = 0;
833

834 835 836 837 838
            std::vector<std::string> vec(4);
            statement st = (sql.prepare <<
                "select str from soci_test order by str", into(vec));
            st.execute();
            while (st.fetch())
839
            {
840
                for (std::size_t j = 0; j != vec.size(); ++j)
841 842 843
                {
                    std::ostringstream ss;
                    ss << "Hello_" << i;
844
                    CHECK(ss.str() == vec[j]);
845 846
                    ++i;
                }
847 848

                vec.resize(4);
849
            }
850 851 852
            CHECK(i == rowsToTest);
        }
    }
853

854
    SECTION("short")
855 856 857 858 859 860
    {
        short const rowsToTest = 100;
        short sh;
        for (sh = 0; sh != rowsToTest; ++sh)
        {
            sql << "insert into soci_test(sh) values(" << sh << ")";
861 862
        }

863 864 865 866
        int count;
        sql << "select count(*) from soci_test", into(count);
        CHECK(count == rowsToTest);

867
        {
868 869 870 871
            short sh2 = 0;

            statement st = (sql.prepare <<
                "select sh from soci_test order by sh", into(sh));
872

873 874
            st.execute();
            while (st.fetch())
875
            {
876 877
                CHECK(sh == sh2);
                ++sh2;
878
            }
879 880 881 882
            CHECK(sh2 == rowsToTest);
        }
        {
            short sh2 = 0;
883

884 885 886 887 888
            std::vector<short> vec(8);
            statement st = (sql.prepare <<
                "select sh from soci_test order by sh", into(vec));
            st.execute();
            while (st.fetch())
889
            {
890
                for (std::size_t i = 0; i != vec.size(); ++i)
891
                {
892
                    CHECK(sh2 == vec[i]);
893 894 895
                    ++sh2;
                }

896
                vec.resize(8);
897
            }
898
            CHECK(sh2 == rowsToTest);
899
        }
900
    }
901

902
    SECTION("int")
903 904 905 906 907 908 909
    {
        int const rowsToTest = 100;
        int i;
        for (i = 0; i != rowsToTest; ++i)
        {
            sql << "insert into soci_test(id) values(" << i << ")";
        }
910

911 912 913
        int count;
        sql << "select count(*) from soci_test", into(count);
        CHECK(count == rowsToTest);
914

915 916
        {
            int i2 = 0;
917

918 919
            statement st = (sql.prepare <<
                "select id from soci_test order by id", into(i));
920

921 922
            st.execute();
            while (st.fetch())
923
            {
924 925 926 927 928 929 930
                CHECK(i == i2);
                ++i2;
            }
            CHECK(i2 == rowsToTest);
        }
        {
            // additional test with the use element
931

932 933
            int i2 = 0;
            int cond = 0; // this condition is always true
934

935 936 937
            statement st = (sql.prepare <<
                "select id from soci_test where id >= :cond order by id",
                use(cond), into(i));
938

939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955
            st.execute();
            while (st.fetch())
            {
                CHECK(i == i2);
                ++i2;
            }
            CHECK(i2 == rowsToTest);
        }
        {
            int i2 = 0;

            std::vector<int> vec(8);
            statement st = (sql.prepare <<
                "select id from soci_test order by id", into(vec));
            st.execute();
            while (st.fetch())
            {
956
                for (std::size_t n = 0; n != vec.size(); ++n)
957
                {
958
                    CHECK(i2 == vec[n]);
959 960
                    ++i2;
                }
961 962

                vec.resize(8);
963
            }
964 965 966
            CHECK(i2 == rowsToTest);
        }
    }
967

968
    SECTION("unsigned int")
969 970 971 972 973 974
    {
        unsigned int const rowsToTest = 100;
        unsigned int ul;
        for (ul = 0; ul != rowsToTest; ++ul)
        {
            sql << "insert into soci_test(ul) values(" << ul << ")";
975 976
        }

977 978 979 980
        int count;
        sql << "select count(*) from soci_test", into(count);
        CHECK(count == static_cast<int>(rowsToTest));

981
        {
982
            unsigned int ul2 = 0;
983

984 985 986 987 988
            statement st = (sql.prepare <<
                "select ul from soci_test order by ul", into(ul));

            st.execute();
            while (st.fetch())
989
            {
990 991
                CHECK(ul == ul2);
                ++ul2;
992
            }
993 994 995 996
            CHECK(ul2 == rowsToTest);
        }
        {
            unsigned int ul2 = 0;
997

998 999 1000 1001 1002
            std::vector<unsigned int> vec(8);
            statement st = (sql.prepare <<
                "select ul from soci_test order by ul", into(vec));
            st.execute();
            while (st.fetch())
1003
            {
1004
                for (std::size_t i = 0; i != vec.size(); ++i)
1005
                {
1006
                    CHECK(ul2 == vec[i]);
1007 1008 1009
                    ++ul2;
                }

1010
                vec.resize(8);
1011
            }
1012
            CHECK(ul2 == rowsToTest);
1013
        }
1014
    }
1015

1016
    SECTION("double")
1017 1018 1019 1020
    {
        int const rowsToTest = 100;
        double d = 0.0;

1021
        statement sti = (sql.prepare <<
1022 1023
            "insert into soci_test(d) values(:d)", use(d));
        for (int i = 0; i != rowsToTest; ++i)
1024
        {
1025
            sti.execute(true);
1026 1027 1028 1029 1030 1031
            d += 0.6;
        }

        int count;
        sql << "select count(*) from soci_test", into(count);
        CHECK(count == rowsToTest);
1032

1033 1034 1035
        {
            double d2 = 0.0;
            int i = 0;
1036 1037

            statement st = (sql.prepare <<
1038 1039 1040 1041
                "select d from soci_test order by d", into(d));

            st.execute();
            while (st.fetch())
1042
            {
1043 1044 1045
                ASSERT_EQUAL(d, d2);
                d2 += 0.6;
                ++i;
1046
            }
1047 1048 1049 1050 1051
            CHECK(i == rowsToTest);
        }
        {
            double d2 = 0.0;
            int i = 0;
1052

1053 1054 1055 1056 1057
            std::vector<double> vec(8);
            statement st = (sql.prepare <<
                "select d from soci_test order by d", into(vec));
            st.execute();
            while (st.fetch())
1058
            {
1059
                for (std::size_t j = 0; j != vec.size(); ++j)
1060
                {
1061
                    ASSERT_EQUAL(d2, vec[j]);
1062 1063 1064 1065
                    d2 += 0.6;
                    ++i;
                }

1066
                vec.resize(8);
1067
            }
1068
            CHECK(i == rowsToTest);
1069
        }
1070 1071
    }

1072
    SECTION("std::tm")
1073 1074 1075
    {
        int const rowsToTest = 8;
        for (int i = 0; i != rowsToTest; ++i)
1076
        {
1077 1078 1079
            std::ostringstream ss;
            ss << 2000 + i << "-0" << 1 + i << '-' << 20 - i << ' '
                << 15 + i << ':' << 50 - i << ':' << 40 + i;
1080

1081 1082 1083
            sql << "insert into soci_test(id, tm) values(" << i
            << ", " << tc_.to_date_time(ss.str()) << ")";
        }
1084

1085 1086 1087 1088 1089
        int count;
        sql << "select count(*) from soci_test", into(count);
        CHECK(count == rowsToTest);

        {
1090
            std::tm t = std::tm();
1091
            int i = 0;
1092

1093 1094
            statement st = (sql.prepare <<
                "select tm from soci_test order by id", into(t));
1095

1096 1097
            st.execute();
            while (st.fetch())
1098
            {
1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111
                CHECK(t.tm_year == 2000 - 1900 + i);
                CHECK(t.tm_mon == i);
                CHECK(t.tm_mday == 20 - i);
                CHECK(t.tm_hour == 15 + i);
                CHECK(t.tm_min == 50 - i);
                CHECK(t.tm_sec == 40 + i);

                ++i;
            }
            CHECK(i == rowsToTest);
        }
        {
            int i = 0;
1112

1113 1114 1115 1116 1117 1118 1119
            std::vector<std::tm> vec(3);
            statement st = (sql.prepare <<
                "select tm from soci_test order by id", into(vec));
            st.execute();
            while (st.fetch())
            {
                for (std::size_t j = 0; j != vec.size(); ++j)
1120
                {
1121 1122 1123 1124 1125 1126
                    CHECK(vec[j].tm_year == 2000 - 1900 + i);
                    CHECK(vec[j].tm_mon == i);
                    CHECK(vec[j].tm_mday == 20 - i);
                    CHECK(vec[j].tm_hour == 15 + i);
                    CHECK(vec[j].tm_min == 50 - i);
                    CHECK(vec[j].tm_sec == 40 + i);
1127 1128 1129 1130

                    ++i;
                }

1131
                vec.resize(3);
1132
            }
1133
            CHECK(i == rowsToTest);
1134 1135 1136
        }
    }
}
msobczak's avatar
msobczak committed
1137

1138
// test for indicators (repeated fetch and bulk)
1139
TEST_CASE_METHOD(common_tests, "Indicators", "[core][indicator]")
1140
{
1141
    soci::session sql(backEndFactory_, connectString_);
1142

1143
    // create and populate the test table
1144
    auto_table_creator tableCreator(tc_.table_creator_1(sql));
1145 1146 1147 1148 1149 1150 1151 1152 1153
    {
        sql << "insert into soci_test(id, val) values(1, 10)";
        sql << "insert into soci_test(id, val) values(2, 11)";
        sql << "insert into soci_test(id, val) values(3, NULL)";
        sql << "insert into soci_test(id, val) values(4, NULL)";
        sql << "insert into soci_test(id, val) values(5, 12)";

        {
            int val;
1154
            indicator ind;
1155

1156
            statement st = (sql.prepare <<
1157 1158 1159
                "select val from soci_test order by id", into(val, ind));

            st.execute();
1160
            bool gotData = st.fetch();
1161 1162 1163
            CHECK(gotData);
            CHECK(ind == i_ok);
            CHECK(val == 10);
1164
            gotData = st.fetch();
1165 1166 1167
            CHECK(gotData);
            CHECK(ind == i_ok);
            CHECK(val == 11);
1168
            gotData = st.fetch();
1169 1170
            CHECK(gotData);
            CHECK(ind == i_null);
1171
            gotData = st.fetch();
1172 1173
            CHECK(gotData);
            CHECK(ind == i_null);
1174
            gotData = st.fetch();
1175 1176 1177
            CHECK(gotData);
            CHECK(ind == i_ok);
            CHECK(val == 12);
1178
            gotData = st.fetch();
1179
            CHECK(gotData == false);
1180 1181 1182
        }
        {
            std::vector<int> vals(3);
1183
            std::vector<indicator> inds(3);
1184

1185
            statement st = (sql.prepare <<
1186 1187 1188
                "select val from soci_test order by id", into(vals, inds));

            st.execute();
1189
            bool gotData = st.fetch();
1190 1191 1192 1193 1194 1195 1196 1197
            CHECK(gotData);
            CHECK(vals.size() == 3);
            CHECK(inds.size() == 3);
            CHECK(inds[0] == i_ok);
            CHECK(vals[0] == 10);
            CHECK(inds[1] == i_ok);
            CHECK(vals[1] == 11);
            CHECK(inds[2] == i_null);
1198
            gotData = st.fetch();
1199 1200 1201 1202 1203
            CHECK(gotData);
            CHECK(vals.size() == 2);
            CHECK(inds[0] == i_null);
            CHECK(inds[1] == i_ok);
            CHECK(vals[1] == 12);
1204
            gotData = st.fetch();
1205
            CHECK(gotData == false);
1206 1207 1208 1209 1210
        }

        // additional test for "no data" condition
        {
            std::vector<int> vals(3);
1211
            std::vector<indicator> inds(3);
1212

1213
            statement st = (sql.prepare <<
1214 1215
                "select val from soci_test where 0 = 1", into(vals, inds));

1216
            bool gotData = st.execute(true);
1217
            CHECK(gotData == false);
1218 1219

            // for convenience, vectors should be truncated
1220 1221
            CHECK(vals.empty());
            CHECK(inds.empty());
1222 1223 1224

            // for even more convenience, fetch should not fail
            // but just report end of rowset
1225
            // (and vectors should be truncated)
1226

1227 1228 1229
            vals.resize(1);
            inds.resize(1);

1230
            gotData = st.fetch();
1231 1232 1233
            CHECK(gotData == false);
            CHECK(vals.empty());
            CHECK(inds.empty());
1234
        }
1235 1236 1237 1238

        // additional test for "no data" without prepared statement
        {
            std::vector<int> vals(3);
1239
            std::vector<indicator> inds(3);
1240 1241 1242 1243 1244

            sql << "select val from soci_test where 0 = 1",
                into(vals, inds);

            // vectors should be truncated
1245 1246
            CHECK(vals.empty());
            CHECK(inds.empty());
1247
        }