Warning, file /sdk/codevis/thirdparty/soci/tests/oracle/test-oracle.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 //
0002 // // Copyright (C) 2004-2007 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 #include "common-tests.h"
0009 #include "soci/soci.h"
0010 #include "soci/oracle/soci-oracle.h"
0011 #include <iostream>
0012 #include <string>
0013 #include <cstdlib>
0014 #include <cstring>
0015 #include <ctime>
0016 
0017 using namespace soci;
0018 using namespace soci::tests;
0019 
0020 std::string connectString;
0021 backend_factory const &backEnd = *soci::factory_oracle();
0022 
0023 // Extra tests for date/time
0024 TEST_CASE("Oracle datetime", "[oracle][datetime]")
0025 {
0026     soci::session sql(backEnd, connectString);
0027 
0028     {
0029         std::time_t now = std::time(NULL);
0030         std::tm t1, t2;
0031         t2 = *std::localtime(&now);
0032 
0033         sql << "select t from (select :t as t from dual)",
0034             into(t1), use(t2);
0035 
0036         CHECK(t1.tm_sec == t2.tm_sec);
0037         CHECK(t1.tm_min == t2.tm_min);
0038         CHECK(t1.tm_hour == t2.tm_hour);
0039         CHECK(t1.tm_mday == t2.tm_mday);
0040         CHECK(t1.tm_mon == t2.tm_mon);
0041         CHECK(t1.tm_year == t2.tm_year);
0042         CHECK(t1.tm_wday == t2.tm_wday);
0043         CHECK(t1.tm_yday == t2.tm_yday);
0044         CHECK(t1.tm_isdst == t2.tm_isdst);
0045 
0046         // make sure the date is stored properly in Oracle
0047         char buf[25];
0048         strftime(buf, sizeof(buf), "%m-%d-%Y %H:%M:%S", &t2);
0049 
0050         std::string t_out;
0051         std::string format("MM-DD-YYYY HH24:MI:SS");
0052         sql << "select to_char(t, :format) from (select :t as t from dual)",
0053             into(t_out), use(format), use(t2);
0054 
0055         CHECK(t_out == std::string(buf));
0056     }
0057 
0058     {
0059         // date and time - before year 2000
0060         std::time_t then = std::time(NULL) - 17*365*24*60*60;
0061         std::tm t1, t2;
0062         t2 = *std::localtime(&then);
0063 
0064         sql << "select t from (select :t as t from dual)",
0065              into(t1), use(t2);
0066 
0067         CHECK(t1.tm_sec == t2.tm_sec);
0068         CHECK(t1.tm_min == t2.tm_min);
0069         CHECK(t1.tm_hour == t2.tm_hour);
0070         CHECK(t1.tm_mday == t2.tm_mday);
0071         CHECK(t1.tm_mon == t2.tm_mon);
0072         CHECK(t1.tm_year == t2.tm_year);
0073         CHECK(t1.tm_wday == t2.tm_wday);
0074         CHECK(t1.tm_yday == t2.tm_yday);
0075         CHECK(t1.tm_isdst == t2.tm_isdst);
0076 
0077         // make sure the date is stored properly in Oracle
0078         char buf[25];
0079         strftime(buf, sizeof(buf), "%m-%d-%Y %H:%M:%S", &t2);
0080 
0081         std::string t_out;
0082         std::string format("MM-DD-YYYY HH24:MI:SS");
0083         sql << "select to_char(t, :format) from (select :t as t from dual)",
0084             into(t_out), use(format), use(t2);
0085 
0086         CHECK(t_out == std::string(buf));
0087     }
0088 }
0089 
0090 // explicit calls test
0091 TEST_CASE("Oracle explicit calls", "[oracle]")
0092 {
0093     soci::session sql(backEnd, connectString);
0094 
0095     statement st(sql);
0096     st.alloc();
0097     int i = 0;
0098     st.exchange(into(i));
0099     st.prepare("select 7 from dual");
0100     st.define_and_bind();
0101     st.execute(1);
0102     CHECK(i == 7);
0103 }
0104 
0105 // DDL + blob test
0106 
0107 struct blob_table_creator : public table_creator_base
0108 {
0109     blob_table_creator(soci::session & sql)
0110     : table_creator_base(sql)
0111     {
0112         sql <<
0113             "create table soci_test ("
0114             "    id number(10) not null,"
0115             "    img blob"
0116             ")";
0117     }
0118 };
0119 
0120 TEST_CASE("Oracle blob", "[oracle][blob]")
0121 {
0122     soci::session sql(backEnd, connectString);
0123 
0124     blob_table_creator tableCreator(sql);
0125 
0126     char buf[] = "abcdefghijklmnopqrstuvwxyz";
0127     sql << "insert into soci_test (id, img) values (7, empty_blob())";
0128 
0129     {
0130         blob b(sql);
0131 
0132         oracle_session_backend *sessionBackEnd
0133             = static_cast<oracle_session_backend *>(sql.get_backend());
0134 
0135         oracle_blob_backend *blobBackEnd
0136             = static_cast<oracle_blob_backend *>(b.get_backend());
0137 
0138         OCILobDisableBuffering(sessionBackEnd->svchp_,
0139             sessionBackEnd->errhp_, blobBackEnd->lobp_);
0140 
0141         sql << "select img from soci_test where id = 7", into(b);
0142         CHECK(b.get_len() == 0);
0143 
0144         b.write_from_start(buf, sizeof(buf));
0145         CHECK(b.get_len() == sizeof(buf));
0146         b.trim(10);
0147         CHECK(b.get_len() == 10);
0148 
0149         // append does not work (Oracle bug #886191 ?)
0150         //b.append(buf, sizeof(buf));
0151         //assert(b.get_len() == sizeof(buf) + 10);
0152         sql.commit();
0153     }
0154 
0155     {
0156         blob b(sql);
0157         sql << "select img from soci_test where id = 7", into(b);
0158         //assert(b.get_len() == sizeof(buf) + 10);
0159         CHECK(b.get_len() == 10);
0160         char buf2[100];
0161         b.read_from_start(buf2, 10);
0162         CHECK(strncmp(buf2, "abcdefghij", 10) == 0);
0163     }
0164 }
0165 
0166 // nested statement test
0167 // (the same syntax is used for output cursors in PL/SQL)
0168 
0169 struct basic_table_creator : public table_creator_base
0170 {
0171     basic_table_creator(soci::session & sql)
0172         : table_creator_base(sql)
0173     {
0174         sql <<
0175                     "create table soci_test ("
0176                     "    id number(5) not null,"
0177                     "    name varchar2(100),"
0178                     "    code number(5)"
0179                     ")";
0180     }
0181 };
0182 
0183 TEST_CASE("Oracle nested statement", "[oracle][blob]")
0184 {
0185     soci::session sql(backEnd, connectString);
0186     basic_table_creator tableCreator(sql);
0187 
0188     int id;
0189     std::string name;
0190     {
0191         statement st1 = (sql.prepare <<
0192             "insert into soci_test (id, name) values (:id, :name)",
0193             use(id), use(name));
0194 
0195         id = 1; name = "John"; st1.execute(1);
0196         id = 2; name = "Anna"; st1.execute(1);
0197         id = 3; name = "Mike"; st1.execute(1);
0198     }
0199 
0200     statement stInner(sql);
0201     statement stOuter = (sql.prepare <<
0202         "select cursor(select name from soci_test order by id)"
0203         " from soci_test where id = 1",
0204         into(stInner));
0205     stInner.exchange(into(name));
0206     stOuter.execute();
0207     stOuter.fetch();
0208 
0209     std::vector<std::string> names;
0210     while (stInner.fetch())    { names.push_back(name); }
0211 
0212     REQUIRE(names.size() == 3);
0213     CHECK(names[0] == "John");
0214     CHECK(names[1] == "Anna");
0215     CHECK(names[2] == "Mike");
0216 }
0217 
0218 
0219 // ROWID test
0220 TEST_CASE("Oracle rowid", "[oracle][rowid]")
0221 {
0222     soci::session sql(backEnd, connectString);
0223     basic_table_creator tableCreator(sql);
0224 
0225     sql << "insert into soci_test(id, name) values(7, \'John\')";
0226 
0227     rowid rid(sql);
0228     sql << "select rowid from soci_test where id = 7", into(rid);
0229 
0230     int id;
0231     std::string name;
0232     sql << "select id, name from soci_test where rowid = :rid",
0233         into(id), into(name), use(rid);
0234 
0235     CHECK(id == 7);
0236     CHECK(name == "John");
0237 }
0238 
0239 // Stored procedures
0240 struct procedure_creator : procedure_creator_base
0241 {
0242     procedure_creator(soci::session & sql)
0243         : procedure_creator_base(sql)
0244     {
0245         sql <<
0246              "create or replace procedure soci_test(output out varchar2,"
0247              "input in varchar2) as "
0248              "begin output := input; end;";
0249     }
0250 };
0251 
0252 TEST_CASE("Oracle stored procedure", "[oracle][stored-procedure]")
0253 {
0254     soci::session sql(backEnd, connectString);
0255     procedure_creator procedure_creator(sql);
0256 
0257     std::string in("my message");
0258     std::string out;
0259     statement st = (sql.prepare <<
0260         "begin soci_test(:output, :input); end;",
0261         use(out, "output"),
0262         use(in, "input"));
0263     st.execute(1);
0264     CHECK(out == in);
0265 
0266     // explicit procedure syntax
0267     {
0268         std::string in("my message2");
0269         std::string out;
0270         procedure proc = (sql.prepare <<
0271             "soci_test(:output, :input)",
0272             use(out, "output"), use(in, "input"));
0273         proc.execute(1);
0274         CHECK(out == in);
0275     }
0276 }
0277 
0278 // bind into user-defined objects
0279 struct string_holder
0280 {
0281     string_holder() {}
0282     string_holder(const char* s) : s_(s) {}
0283     string_holder(std::string s) : s_(s) {}
0284     std::string get() const { return s_; }
0285 private:
0286     std::string s_;
0287 };
0288 
0289 namespace soci
0290 {
0291     template <>
0292     struct type_conversion<string_holder>
0293     {
0294         typedef std::string base_type;
0295         static void from_base(const std::string &s, indicator /* ind */,
0296             string_holder &sh)
0297         {
0298             sh = string_holder(s);
0299         }
0300 
0301         static void to_base(const string_holder &sh, std::string &s, indicator &ind)
0302         {
0303             s = sh.get();
0304             ind = i_ok;
0305         }
0306     };
0307 }
0308 
0309 struct in_out_procedure_creator : public procedure_creator_base
0310 {
0311     in_out_procedure_creator(soci::session & sql)
0312         : procedure_creator_base(sql)
0313     {
0314         sql << "create or replace procedure soci_test(s in out varchar2)"
0315                 " as begin s := s || s; end;";
0316     }
0317 };
0318 
0319 struct returns_null_procedure_creator : public procedure_creator_base
0320 {
0321     returns_null_procedure_creator(soci::session & sql)
0322         : procedure_creator_base(sql)
0323     {
0324         sql << "create or replace procedure soci_test(s in out varchar2)"
0325             " as begin s := NULL; end;";
0326     }
0327 };
0328 
0329 TEST_CASE("Oracle user-defined objects", "[oracle][type_conversion]")
0330 {
0331     soci::session sql(backEnd, connectString);
0332     {
0333         basic_table_creator tableCreator(sql);
0334 
0335         int id(1);
0336         string_holder in("my string");
0337         sql << "insert into soci_test(id, name) values(:id, :name)", use(id), use(in);
0338 
0339         string_holder out;
0340         sql << "select name from soci_test", into(out);
0341         CHECK(out.get() == "my string");
0342 
0343         row r;
0344         sql << "select * from soci_test", into(r);
0345         string_holder dynamicOut = r.get<string_holder>(1);
0346         CHECK(dynamicOut.get() == "my string");
0347     }
0348 }
0349 
0350 TEST_CASE("Oracle user-defined objects in/out", "[oracle][type_conversion]")
0351 {
0352     soci::session sql(backEnd, connectString);
0353 
0354     // test procedure with user-defined type as in-out parameter
0355     {
0356         in_out_procedure_creator procedureCreator(sql);
0357 
0358         std::string sh("test");
0359         procedure proc = (sql.prepare << "soci_test(:s)", use(sh));
0360         proc.execute(1);
0361         CHECK(sh == "testtest");
0362     }
0363 
0364     // test procedure with user-defined type as in-out parameter
0365     {
0366         in_out_procedure_creator procedureCreator(sql);
0367 
0368         string_holder sh("test");
0369         procedure proc = (sql.prepare << "soci_test(:s)", use(sh));
0370         proc.execute(1);
0371         CHECK(sh.get() == "testtest");
0372     }
0373 }
0374 
0375 TEST_CASE("Oracle null user-defined objects in/out", "[oracle][null][type_conversion]")
0376 {
0377     soci::session sql(backEnd, connectString);
0378 
0379     // test procedure which returns null
0380     returns_null_procedure_creator procedureCreator(sql);
0381 
0382     string_holder sh;
0383     indicator ind = i_ok;
0384     procedure proc = (sql.prepare << "soci_test(:s)", use(sh, ind));
0385     proc.execute(1);
0386     CHECK(ind == i_null);
0387 }
0388 
0389 // test bulk insert features
0390 TEST_CASE("Oracle bulk insert", "[oracle][insert][bulk]")
0391 {
0392     soci::session sql(backEnd, connectString);
0393 
0394     basic_table_creator tableCreator(sql);
0395 
0396     // verify exception is thrown if vectors of unequal size are passed in
0397     {
0398         std::vector<int> ids;
0399         ids.push_back(1);
0400         ids.push_back(2);
0401         std::vector<int> codes;
0402         codes.push_back(1);
0403 
0404         try
0405         {
0406             sql << "insert into soci_test(id,code) values(:id,:code)",
0407                 use(ids), use(codes);
0408             FAIL("expected exception not thrown");
0409         }
0410         catch (soci_error const &e)
0411         {
0412             std::string const error = e.what();
0413             CAPTURE(error);
0414             CHECK(error.find("Bind variable size mismatch")
0415                 != std::string::npos);
0416         }
0417 
0418         try
0419         {
0420             sql << "select from soci_test", into(ids), into(codes);
0421             FAIL("expected exception not thrown");
0422         }
0423         catch (std::exception const &e)
0424         {
0425             std::string const error = e.what();
0426             CAPTURE(error);
0427             CHECK(error.find("Bind variable size mismatch")
0428                 != std::string::npos);
0429         }
0430     }
0431 
0432     // verify partial insert occurs when one of the records is bad
0433     {
0434         std::vector<int> ids;
0435         ids.push_back(100);
0436         ids.push_back(1000000); // too big for column
0437 
0438         try
0439         {
0440             sql << "insert into soci_test (id) values(:id)", use(ids, "id");
0441             FAIL("expected exception not thrown");
0442         }
0443         catch (soci_error const &e)
0444         {
0445             std::string const error = e.what();
0446             //TODO e could be made to tell which row(s) failed
0447             CAPTURE(error);
0448             CHECK(error.find("ORA-01438") != std::string::npos);
0449         }
0450         sql.commit();
0451         int count(7);
0452         sql << "select count(*) from soci_test", into(count);
0453         CHECK(count == 1);
0454         sql << "delete from soci_test";
0455     }
0456 
0457     // test insert
0458     {
0459         std::vector<int> ids;
0460         for (int i = 0; i != 3; ++i)
0461         {
0462             ids.push_back(i+10);
0463         }
0464 
0465         statement st = (sql.prepare << "insert into soci_test(id) values(:id)",
0466                             use(ids));
0467         st.execute(1);
0468         int count;
0469         sql << "select count(*) from soci_test", into(count);
0470         CHECK(count == 3);
0471     }
0472 
0473     //verify an exception is thrown if into vector is zero length
0474     {
0475         std::vector<int> ids;
0476         CHECK_THROWS_AS((sql << "select id from soci_test", into(ids)), soci_error);
0477     }
0478 
0479     // verify an exception is thrown if use vector is zero length
0480     {
0481         std::vector<int> ids;
0482         CHECK_THROWS_AS((sql << "insert into soci_test(id) values(:id)", use(ids)), soci_error);
0483     }
0484 
0485     // test "no data" condition
0486     {
0487         std::vector<indicator> inds(3);
0488         std::vector<int> ids_out(3);
0489         statement st = (sql.prepare << "select id from soci_test where 1=0",
0490                         into(ids_out, inds));
0491 
0492         // false return value means "no data"
0493         CHECK(st.execute(1) == false);
0494 
0495         // that's it - nothing else is guaranteed
0496         // and nothing else is to be tested here
0497     }
0498 
0499     // test NULL indicators
0500     {
0501         std::vector<int> ids(3);
0502         sql << "select id from soci_test", into(ids);
0503 
0504         std::vector<indicator> inds_in;
0505         inds_in.push_back(i_ok);
0506         inds_in.push_back(i_null);
0507         inds_in.push_back(i_ok);
0508 
0509         std::vector<int> new_codes;
0510         new_codes.push_back(10);
0511         new_codes.push_back(11);
0512         new_codes.push_back(10);
0513 
0514         sql << "update soci_test set code = :code where id = :id",
0515                 use(new_codes, inds_in), use(ids);
0516 
0517         std::vector<indicator> inds_out(3);
0518         std::vector<int> codes(3);
0519 
0520         sql << "select code from soci_test", into(codes, inds_out);
0521         REQUIRE(codes.size() == 3);
0522         REQUIRE(inds_out.size() == 3);
0523         CHECK(codes[0] == 10);
0524         CHECK(codes[2] == 10);
0525         CHECK(inds_out[0] == i_ok);
0526         CHECK(inds_out[1] == i_null);
0527         CHECK(inds_out[2] == i_ok);
0528     }
0529 
0530     // verify an exception is thrown if null is selected
0531     //  and no indicator was provided
0532     {
0533         std::string msg;
0534         std::vector<int> intos(3);
0535         try
0536         {
0537             sql << "select code from soci_test", into(intos);
0538             FAIL("expected exception not thrown");
0539         }
0540         catch (soci_error const &e)
0541         {
0542             CHECK(e.get_error_message() ==
0543                 "Null value fetched and no indicator defined." );
0544         }
0545     }
0546 
0547     // test basic select
0548     {
0549         const size_t sz = 3;
0550         std::vector<indicator> inds(sz);
0551         std::vector<int> ids_out(sz);
0552         statement st = (sql.prepare << "select id from soci_test",
0553             into(ids_out, inds));
0554         const bool gotData = st.execute(true);
0555         CHECK(gotData);
0556         REQUIRE(ids_out.size() == sz);
0557         CHECK(ids_out[0] == 10);
0558         CHECK(ids_out[2] == 12);
0559         REQUIRE(inds.size() == 3);
0560         CHECK(inds[0] == i_ok);
0561         CHECK(inds[1] == i_ok);
0562         CHECK(inds[2] == i_ok);
0563     }
0564 
0565     // verify execute(0)
0566     {
0567         std::vector<int> ids_out(2);
0568         statement st = (sql.prepare << "select id from soci_test",
0569             into(ids_out));
0570 
0571         st.execute();
0572         REQUIRE(ids_out.size() == 2);
0573         bool gotData = st.fetch();
0574         CHECK(gotData);
0575         REQUIRE(ids_out.size() == 2);
0576         CHECK(ids_out[0] == 10);
0577         CHECK(ids_out[1] == 11);
0578         gotData = st.fetch();
0579         CHECK(gotData);
0580         REQUIRE(ids_out.size() == 1);
0581         CHECK(ids_out[0] == 12);
0582         gotData = st.fetch();
0583         CHECK(gotData == false);
0584     }
0585 
0586     // verify resizing happens if vector is larger
0587     // than number of rows returned
0588     {
0589         std::vector<int> ids_out(4); // one too many
0590         statement st2 = (sql.prepare << "select id from soci_test",
0591             into(ids_out));
0592         bool gotData = st2.execute(true);
0593         CHECK(gotData);
0594         REQUIRE(ids_out.size() == 3);
0595         CHECK(ids_out[0] == 10);
0596         CHECK(ids_out[2] == 12);
0597     }
0598 
0599     // verify resizing happens properly during fetch()
0600     {
0601         std::vector<int> more;
0602         more.push_back(13);
0603         more.push_back(14);
0604         sql << "insert into soci_test(id) values(:id)", use(more);
0605 
0606         std::vector<int> ids(2);
0607         statement st3 = (sql.prepare << "select id from soci_test", into(ids));
0608         bool gotData = st3.execute(true);
0609         CHECK(gotData);
0610         CHECK(ids[0] == 10);
0611         CHECK(ids[1] == 11);
0612 
0613         gotData = st3.fetch();
0614         CHECK(gotData);
0615         CHECK(ids[0] == 12);
0616         CHECK(ids[1] == 13);
0617 
0618         gotData = st3.fetch();
0619         CHECK(gotData);
0620         REQUIRE(ids.size() == 1);
0621         CHECK(ids[0] == 14);
0622 
0623         gotData = st3.fetch();
0624         CHECK(gotData == false);
0625     }
0626 }
0627 
0628 // more tests for bulk fetch
0629 TEST_CASE("Oracle bulk fetch", "[oracle][fetch][bulk]")
0630 {
0631     soci::session sql(backEnd, connectString);
0632 
0633     basic_table_creator tableCreator(sql);
0634 
0635     std::vector<int> in;
0636     for (int i = 1; i <= 10; ++i)
0637     {
0638         in.push_back(i);
0639     }
0640 
0641     sql << "insert into soci_test (id) values(:id)", use(in);
0642 
0643     int count(0);
0644     sql << "select count(*) from soci_test", into(count);
0645     CHECK(count == 10);
0646 
0647     // verify that the exception is thrown when trying to resize
0648     // the output vector to the size that is bigger than that
0649     // at the time of binding
0650     {
0651         std::vector<int> out(4);
0652         statement st = (sql.prepare <<
0653             "select id from soci_test", into(out));
0654 
0655         st.execute();
0656 
0657         st.fetch();
0658         REQUIRE(out.size() == 4);
0659         CHECK(out[0] == 1);
0660         CHECK(out[1] == 2);
0661         CHECK(out[2] == 3);
0662         CHECK(out[3] == 4);
0663         out.resize(5); // this should be detected as error
0664         try
0665         {
0666             st.fetch();
0667             FAIL("expected exception not thrown");
0668         }
0669         catch (soci_error const &e)
0670         {
0671             CHECK(e.get_error_message() ==
0672                 "Increasing the size of the output vector is not supported.");
0673         }
0674     }
0675 
0676     // on the other hand, downsizing is OK
0677     {
0678         std::vector<int> out(4);
0679         statement st = (sql.prepare <<
0680             "select id from soci_test", into(out));
0681 
0682         st.execute();
0683 
0684         st.fetch();
0685         REQUIRE(out.size() == 4);
0686         CHECK(out[0] == 1);
0687         CHECK(out[1] == 2);
0688         CHECK(out[2] == 3);
0689         CHECK(out[3] == 4);
0690         out.resize(3); // ok
0691         st.fetch();
0692         REQUIRE(out.size() == 3);
0693         CHECK(out[0] == 5);
0694         CHECK(out[1] == 6);
0695         CHECK(out[2] == 7);
0696         out.resize(4); // ok, not bigger than initially
0697         st.fetch();
0698         REQUIRE(out.size() == 3); // downsized because of end of data
0699         CHECK(out[0] == 8);
0700         CHECK(out[1] == 9);
0701         CHECK(out[2] == 10);
0702         bool gotData = st.fetch();
0703         CHECK(gotData == false); // end of data
0704     }
0705 }
0706 
0707 struct person
0708 {
0709     int id;
0710     std::string firstName;
0711     string_holder lastName; //test mapping of type_conversion-based types
0712     std::string gender;
0713 };
0714 
0715 // Object-Relational Mapping
0716 // Note: Use the values class as shown below in type_conversions
0717 // to achieve object relational mapping.  The values class should
0718 // not be used directly in any other fashion.
0719 namespace soci
0720 {
0721     // name-based conversion
0722     template<> struct type_conversion<person>
0723     {
0724         typedef values base_type;
0725 
0726         static void from_base(values const &v, indicator /* ind */, person &p)
0727         {
0728             // ignoring possibility that the whole object might be NULL
0729 
0730             p.id = v.get<int>("ID");
0731             p.firstName = v.get<std::string>("FIRST_NAME");
0732             p.lastName = v.get<string_holder>("LAST_NAME");
0733             p.gender = v.get<std::string>("GENDER", "unknown");
0734         }
0735 
0736         static void to_base(person const & p, values & v, indicator & ind)
0737         {
0738             v.set("ID", p.id);
0739             v.set("FIRST_NAME", p.firstName);
0740             v.set("LAST_NAME", p.lastName);
0741             v.set("GENDER", p.gender, p.gender.empty() ? i_null : i_ok);
0742             ind = i_ok;
0743         }
0744     };
0745 }
0746 
0747 struct person_table_creator : public table_creator_base
0748 {
0749     person_table_creator(soci::session & sql)
0750         : table_creator_base(sql)
0751     {
0752         sql << "create table soci_test(id numeric(5,0) NOT NULL,"
0753              << " last_name varchar2(20), first_name varchar2(20), "
0754                 " gender varchar2(10))";
0755     }
0756 };
0757 
0758 struct times100_procedure_creator : public procedure_creator_base
0759 {
0760     times100_procedure_creator(soci::session & sql)
0761         : procedure_creator_base(sql)
0762     {
0763         sql << "create or replace procedure soci_test(id in out number)"
0764                " as begin id := id * 100; end;";
0765     }
0766 };
0767 
0768 TEST_CASE("Oracle ORM", "[oracle][orm]")
0769 {
0770     soci::session sql(backEnd, connectString);
0771 
0772     {
0773         person_table_creator tableCreator(sql);
0774 
0775         person p;
0776         p.id = 1;
0777         p.lastName = "Smith";
0778         p.firstName = "Pat";
0779         sql << "insert into soci_test(id, first_name, last_name, gender) "
0780             << "values(:ID, :FIRST_NAME, :LAST_NAME, :GENDER)", use(p);
0781 
0782         // p should be unchanged
0783         CHECK(p.id == 1);
0784         CHECK(p.firstName == "Pat");
0785         CHECK(p.lastName.get() == "Smith");
0786 
0787         person p1;
0788         sql << "select * from soci_test", into(p1);
0789         CHECK(p1.id == 1);
0790         CHECK(p1.firstName == "Pat");
0791         CHECK(p1.lastName.get() == "Smith");
0792         CHECK(p1.gender == "unknown");
0793 
0794         p.firstName = "Patricia";
0795         sql << "update soci_test set first_name = :FIRST_NAME "
0796                "where id = :ID", use(p);
0797 
0798         // p should be unchanged
0799         CHECK(p.id == 1);
0800         CHECK(p.firstName == "Patricia");
0801         CHECK(p.lastName.get() == "Smith");
0802         // Note: gender is now "unknown" because of the mapping, not ""
0803         CHECK(p.gender == "unknown");
0804 
0805         person p2;
0806         sql << "select * from soci_test", into(p2);
0807         CHECK(p2.id == 1);
0808         CHECK(p2.firstName == "Patricia");
0809         CHECK(p2.lastName.get() == "Smith");
0810 
0811         // insert a second row so we can test fetching
0812         person p3;
0813         p3.id = 2;
0814         p3.firstName = "Joe";
0815         p3.lastName = "Smith";
0816         sql << "insert into soci_test(id, first_name, last_name, gender) "
0817             << "values(:ID, :FIRST_NAME, :LAST_NAME, :GENDER)", use(p3);
0818 
0819         person p4;
0820         statement st = (sql.prepare << "select * from soci_test order by id",
0821                     into(p4));
0822 
0823         st.execute();
0824         bool gotData = st.fetch();
0825         CHECK(gotData);
0826         CHECK(p4.id == 1);
0827         CHECK(p4.firstName == "Patricia");
0828 
0829         gotData = st.fetch();
0830         CHECK(gotData);
0831         CHECK(p4.id == 2);
0832         CHECK(p4.firstName == "Joe");
0833         gotData = st.fetch();
0834         CHECK(gotData == false);
0835     }
0836 
0837     // test with stored procedure
0838     {
0839         times100_procedure_creator procedureCreator(sql);
0840 
0841         person p;
0842         p.id = 1;
0843         p.firstName = "Pat";
0844         p.lastName = "Smith";
0845         procedure proc = (sql.prepare << "soci_test(:ID)", use(p));
0846         proc.execute(1);
0847         CHECK(p.id == 100);
0848         CHECK(p.firstName == "Pat");
0849         CHECK(p.lastName.get() == "Smith");
0850     }
0851 
0852     // test with stored procedure which returns null
0853     {
0854         returns_null_procedure_creator procedureCreator(sql);
0855 
0856         person p;
0857         try
0858         {
0859             procedure proc = (sql.prepare << "soci_test(:FIRST_NAME)",
0860                                 use(p));
0861             proc.execute(1);
0862             FAIL("expected exception not thrown");
0863         }
0864         catch (soci_error& e)
0865         {
0866             CHECK(e.get_error_message() ==
0867                 "Null value not allowed for this type");
0868         }
0869 
0870         procedure proc = (sql.prepare << "soci_test(:GENDER)",
0871                                 use(p));
0872         proc.execute(1);
0873         CHECK(p.gender == "unknown");
0874 
0875     }
0876 }
0877 
0878 // Experimental support for position based O/R Mapping
0879 
0880 // additional type for position-based test
0881 struct person2
0882 {
0883     int id;
0884     std::string firstName;
0885     std::string lastName;
0886     std::string gender;
0887 };
0888 
0889 // additional type for stream-like test
0890 struct person3 : person2 {};
0891 
0892 namespace soci
0893 {
0894     // position-based conversion
0895     template<> struct type_conversion<person2>
0896     {
0897         typedef values base_type;
0898 
0899         static void from_base(values const &v, indicator /* ind */, person2 &p)
0900         {
0901             p.id = v.get<int>(0);
0902             p.firstName = v.get<std::string>(1);
0903             p.lastName = v.get<std::string>(2);
0904             p.gender = v.get<std::string>(3, "whoknows");
0905         }
0906 
0907         // What about the "to" part? Does it make any sense to have it?
0908     };
0909 
0910     // stream-like conversion
0911     template<> struct type_conversion<person3>
0912     {
0913         typedef values base_type;
0914 
0915         static void from_base(values const &v, indicator /* ind */, person3 &p)
0916         {
0917             v >> p.id >> p.firstName >> p.lastName >> p.gender;
0918         }
0919         // TODO: The "to" part is certainly needed.
0920     };
0921 }
0922 
0923 TEST_CASE("Oracle ORM by index", "[oracle][orm]")
0924 {
0925     soci::session sql(backEnd, connectString);
0926 
0927     person_table_creator tableCreator(sql);
0928 
0929     person p;
0930     p.id = 1;
0931     p.lastName = "Smith";
0932     p.firstName = "Patricia";
0933     sql << "insert into soci_test(id, first_name, last_name, gender) "
0934         << "values(:ID, :FIRST_NAME, :LAST_NAME, :GENDER)", use(p);
0935 
0936     //  test position-based conversion
0937     person2 p3;
0938     sql << "select id, first_name, last_name, gender from soci_test", into(p3);
0939     CHECK(p3.id == 1);
0940     CHECK(p3.firstName == "Patricia");
0941     CHECK(p3.lastName == "Smith");
0942     CHECK(p3.gender == "whoknows");
0943 
0944     sql << "update soci_test set gender = 'F' where id = 1";
0945 
0946     // additional test for stream-like conversion
0947     person3 p4;
0948     sql << "select id, first_name, last_name, gender from soci_test", into(p4);
0949     CHECK(p4.id == 1);
0950     CHECK(p4.firstName == "Patricia");
0951     CHECK(p4.lastName == "Smith");
0952     CHECK(p4.gender == "F");
0953 }
0954 
0955 //
0956 // Backwards compatibility - support use of large strings with
0957 // columns of type LONG
0958 ///
0959 struct long_table_creator : public table_creator_base
0960 {
0961     long_table_creator(soci::session & sql)
0962         : table_creator_base(sql)
0963     {
0964         sql << "create table soci_test(l long)";
0965     }
0966 };
0967 
0968 TEST_CASE("Oracle large strings as long", "[oracle][compatibility]")
0969 {
0970     soci::session sql(backEnd, connectString);
0971     long_table_creator creator(sql);
0972 
0973     const std::string::size_type max = 32768;
0974     std::string in(max, 'X');
0975 
0976     sql << "insert into soci_test values(:l)", use(in);
0977 
0978     std::string out;
0979     sql << "select l from soci_test", into(out);
0980 
0981     CHECK(out.size() == max);
0982     CHECK(in == out);
0983 }
0984 
0985 // test for modifiable and const use elements
0986 TEST_CASE("Oracle const and modifiable parameters", "[oracle][use]")
0987 {
0988     soci::session sql(backEnd, connectString);
0989 
0990     int i = 7;
0991     sql << "begin "
0992         "select 2 * :i into :i from dual; "
0993         "end;", use(i);
0994     CHECK(i == 14);
0995 
0996     const int j = 7;
0997     try
0998     {
0999         sql << "begin "
1000             "select 2 * :i into :i from dual;"
1001             " end;", use(j);
1002 
1003         FAIL("expected exception not thrown");
1004     }
1005     catch (soci_error const & e)
1006     {
1007         CHECK(e.get_error_message() ==
1008             "Attempted modification of const use element");
1009     }
1010 }
1011 
1012 struct longlong_table_creator : table_creator_base
1013 {
1014     longlong_table_creator(soci::session & sql)
1015         : table_creator_base(sql)
1016     {
1017         sql << "create table soci_test(val number(20))";
1018     }
1019 };
1020 
1021 // test using the result of to_number() which uses the default NUMBER type,
1022 // with precision == scale == 0, with rowset
1023 TEST_CASE("Oracle to_number with rowset", "[oracle][rowset][to_number]")
1024 {
1025     soci::session sql(backEnd, connectString);
1026 
1027     soci::rowset<soci::row>
1028         rs = (sql.prepare << "select to_number('123456789012345') from dual");
1029     double d = rs.begin()->get<double>(0);
1030     ASSERT_EQUAL_EXACT(d, 123456789012345);
1031 
1032     rs = (sql.prepare << "select to_number(:t) from dual", use(3.14));
1033     d = rs.begin()->get<double>(0);
1034     ASSERT_EQUAL_EXACT(d, 3.14);
1035 }
1036 
1037 // long long test
1038 TEST_CASE("Oracle long long", "[oracle][longlong]")
1039 {
1040     {
1041         soci::session sql(backEnd, connectString);
1042 
1043         longlong_table_creator tableCreator(sql);
1044 
1045         long long v1 = 1000000000000LL;
1046         sql << "insert into soci_test(val) values(:val)", use(v1);
1047 
1048         long long v2 = 0LL;
1049         sql << "select val from soci_test", into(v2);
1050 
1051         CHECK(v2 == v1);
1052     }
1053 
1054     // vector<long long>
1055     {
1056         soci::session sql(backEnd, connectString);
1057 
1058         longlong_table_creator tableCreator(sql);
1059 
1060         std::vector<long long> v1;
1061         v1.push_back(1000000000000LL);
1062         v1.push_back(1000000000001LL);
1063         v1.push_back(1000000000002LL);
1064         v1.push_back(1000000000003LL);
1065         v1.push_back(1000000000004LL);
1066 
1067         sql << "insert into soci_test(val) values(:val)", use(v1);
1068 
1069         std::vector<long long> v2(10);
1070         sql << "select val from soci_test order by val desc", into(v2);
1071 
1072         REQUIRE(v2.size() == 5);
1073         CHECK(v2[0] == 1000000000004LL);
1074         CHECK(v2[1] == 1000000000003LL);
1075         CHECK(v2[2] == 1000000000002LL);
1076         CHECK(v2[3] == 1000000000001LL);
1077         CHECK(v2[4] == 1000000000000LL);
1078     }
1079 }
1080 
1081 // Test the DDL and metadata functionality
1082 TEST_CASE("Oracle DDL with metadata", "[oracle][ddl]")
1083 {
1084     soci::session sql(backEnd, connectString);
1085 
1086     // note: prepare_column_descriptions expects l-value
1087     std::string ddl_t1 = "DDL_T1";
1088     std::string ddl_t2 = "DDL_T2";
1089     std::string ddl_t3 = "DDL_T3";
1090 
1091     // single-expression variant:
1092     sql.create_table(ddl_t1).column("I", soci::dt_integer).column("J", soci::dt_integer);
1093 
1094     // check whether this table was created:
1095 
1096     bool ddl_t1_found = false;
1097     bool ddl_t2_found = false;
1098     bool ddl_t3_found = false;
1099     std::string table_name;
1100     soci::statement st = (sql.prepare_table_names(), into(table_name));
1101     st.execute();
1102     while (st.fetch())
1103     {
1104         if (table_name == ddl_t1) { ddl_t1_found = true; }
1105         if (table_name == ddl_t2) { ddl_t2_found = true; }
1106         if (table_name == ddl_t3) { ddl_t3_found = true; }
1107     }
1108 
1109     CHECK(ddl_t1_found);
1110     CHECK(ddl_t2_found == false);
1111     CHECK(ddl_t3_found == false);
1112 
1113     // check whether ddl_t1 has the right structure:
1114 
1115     bool i_found = false;
1116     bool j_found = false;
1117     bool other_found = false;
1118     soci::column_info ci;
1119     soci::statement st1 = (sql.prepare_column_descriptions(ddl_t1), into(ci));
1120     st1.execute();
1121     while (st1.fetch())
1122     {
1123         if (ci.name == "I")
1124         {
1125             CHECK(ci.type == soci::dt_integer);
1126             CHECK(ci.nullable);
1127             i_found = true;
1128         }
1129         else if (ci.name == "J")
1130         {
1131             CHECK(ci.type == soci::dt_integer);
1132             CHECK(ci.nullable);
1133             j_found = true;
1134         }
1135         else
1136         {
1137             other_found = true;
1138         }
1139     }
1140 
1141     CHECK(i_found);
1142     CHECK(j_found);
1143     CHECK(other_found == false);
1144 
1145     // two more tables:
1146 
1147     // separately defined columns:
1148     // (note: statement is executed when ddl object goes out of scope)
1149     {
1150         soci::ddl_type ddl = sql.create_table(ddl_t2);
1151         ddl.column("I", soci::dt_integer);
1152         ddl.column("J", soci::dt_integer);
1153         ddl.column("K", soci::dt_integer)("not null");
1154         ddl.primary_key("t2_pk", "J");
1155     }
1156 
1157     sql.add_column(ddl_t1, "K", soci::dt_integer);
1158     sql.add_column(ddl_t1, "BIG", soci::dt_string, 0); // "unlimited" length -> CLOB
1159     sql.drop_column(ddl_t1, "I");
1160 
1161     // or with constraint as in t2:
1162     sql.add_column(ddl_t2, "M", soci::dt_integer)("not null");
1163 
1164     // third table with a foreign key to the second one
1165     {
1166         soci::ddl_type ddl = sql.create_table(ddl_t3);
1167         ddl.column("X", soci::dt_integer);
1168         ddl.column("Y", soci::dt_integer);
1169         ddl.foreign_key("t3_fk", "X", ddl_t2, "J");
1170     }
1171 
1172     // check if all tables were created:
1173 
1174     ddl_t1_found = false;
1175     ddl_t2_found = false;
1176     ddl_t3_found = false;
1177     soci::statement st2 = (sql.prepare_table_names(), into(table_name));
1178     st2.execute();
1179     while (st2.fetch())
1180     {
1181         if (table_name == ddl_t1) { ddl_t1_found = true; }
1182         if (table_name == ddl_t2) { ddl_t2_found = true; }
1183         if (table_name == ddl_t3) { ddl_t3_found = true; }
1184     }
1185 
1186     CHECK(ddl_t1_found);
1187     CHECK(ddl_t2_found);
1188     CHECK(ddl_t3_found);
1189 
1190     // check if ddl_t1 has the right structure (it was altered):
1191 
1192     i_found = false;
1193     j_found = false;
1194     bool k_found = false;
1195     bool big_found = false;
1196     other_found = false;
1197     soci::statement st3 = (sql.prepare_column_descriptions(ddl_t1), into(ci));
1198     st3.execute();
1199     while (st3.fetch())
1200     {
1201         if (ci.name == "J")
1202         {
1203             CHECK(ci.type == soci::dt_integer);
1204             CHECK(ci.nullable);
1205             j_found = true;
1206         }
1207         else if (ci.name == "K")
1208         {
1209             CHECK(ci.type == soci::dt_integer);
1210             CHECK(ci.nullable);
1211             k_found = true;
1212         }
1213         else if (ci.name == "BIG")
1214         {
1215             CHECK(ci.type == soci::dt_string);
1216             CHECK(ci.precision == 0); // "unlimited" for strings
1217             big_found = true;
1218         }
1219         else
1220         {
1221             other_found = true;
1222         }
1223     }
1224 
1225     CHECK(i_found == false);
1226     CHECK(j_found);
1227     CHECK(k_found);
1228     CHECK(big_found);
1229     CHECK(other_found == false);
1230 
1231     // check if ddl_t2 has the right structure:
1232 
1233     i_found = false;
1234     j_found = false;
1235     k_found = false;
1236     bool m_found = false;
1237     other_found = false;
1238     soci::statement st4 = (sql.prepare_column_descriptions(ddl_t2), into(ci));
1239     st4.execute();
1240     while (st4.fetch())
1241     {
1242         if (ci.name == "I")
1243         {
1244             CHECK(ci.type == soci::dt_integer);
1245             CHECK(ci.nullable);
1246             i_found = true;
1247         }
1248         else if (ci.name == "J")
1249         {
1250             CHECK(ci.type == soci::dt_integer);
1251             CHECK(ci.nullable == false); // primary key
1252             j_found = true;
1253         }
1254         else if (ci.name == "K")
1255         {
1256             CHECK(ci.type == soci::dt_integer);
1257             CHECK(ci.nullable == false);
1258             k_found = true;
1259         }
1260         else if (ci.name == "M")
1261         {
1262             CHECK(ci.type == soci::dt_integer);
1263             CHECK(ci.nullable == false);
1264             m_found = true;
1265         }
1266         else
1267         {
1268             other_found = true;
1269         }
1270     }
1271 
1272     CHECK(i_found);
1273     CHECK(j_found);
1274     CHECK(k_found);
1275     CHECK(m_found);
1276     CHECK(other_found == false);
1277 
1278     sql.drop_table(ddl_t1);
1279     sql.drop_table(ddl_t3); // note: this must be dropped before ddl_t2
1280     sql.drop_table(ddl_t2);
1281 
1282     // check if all tables were dropped:
1283 
1284     ddl_t1_found = false;
1285     ddl_t2_found = false;
1286     ddl_t3_found = false;
1287     st2 = (sql.prepare_table_names(), into(table_name));
1288     st2.execute();
1289     while (st2.fetch())
1290     {
1291         if (table_name == ddl_t1) { ddl_t1_found = true; }
1292         if (table_name == ddl_t2) { ddl_t2_found = true; }
1293         if (table_name == ddl_t3) { ddl_t3_found = true; }
1294     }
1295 
1296     CHECK(ddl_t1_found == false);
1297     CHECK(ddl_t2_found == false);
1298     CHECK(ddl_t3_found == false);
1299 
1300     int i = -1;
1301     sql << "select length(" + sql.empty_blob() + ") from dual", into(i);
1302     CHECK(i == 0);
1303     sql << "select " + sql.nvl() + "(1, 2) from dual", into(i);
1304     CHECK(i == 1);
1305     sql << "select " + sql.nvl() + "(NULL, 2) from dual", into(i);
1306     CHECK(i == 2);
1307 }
1308 
1309 // Test the bulk iterators functionality
1310 TEST_CASE("Bulk iterators", "[oracle][bulkiters]")
1311 {
1312     soci::session sql(backEnd, connectString);
1313 
1314     sql << "create table t (i integer)";
1315 
1316     // test bulk iterators with basic types
1317     {
1318         std::vector<int> v;
1319         v.push_back(10);
1320         v.push_back(20);
1321         v.push_back(30);
1322         v.push_back(40);
1323         v.push_back(50);
1324 
1325         std::size_t begin = 2;
1326         std::size_t end = 5;
1327         sql << "insert into t (i) values (:v)", soci::use(v, begin, end);
1328 
1329         v.clear();
1330         v.resize(20);
1331         begin = 5;
1332         end = 20;
1333         sql << "select i from t", soci::into(v, begin, end);
1334 
1335         CHECK(end == 8);
1336         for (std::size_t i = 0; i != 5; ++i)
1337         {
1338             CHECK(v[i] == 0);
1339         }
1340         CHECK(v[5] == 30);
1341         CHECK(v[6] == 40);
1342         CHECK(v[7] == 50);
1343         for (std::size_t i = end; i != 20; ++i)
1344         {
1345             CHECK(v[i] == 0);
1346         }
1347     }
1348 
1349     sql << "delete from t";
1350 
1351     // test bulk iterators with user types
1352     {
1353         std::vector<MyInt> v;
1354         v.push_back(MyInt(10));
1355         v.push_back(MyInt(20));
1356         v.push_back(MyInt(30));
1357         v.push_back(MyInt(40));
1358         v.push_back(MyInt(50));
1359 
1360         std::size_t begin = 2;
1361         std::size_t end = 5;
1362         sql << "insert into t (i) values (:v)", soci::use(v, begin, end);
1363 
1364         v.clear();
1365         for (std::size_t i = 0; i != 20; ++i)
1366         {
1367             v.push_back(MyInt(-1));
1368         }
1369 
1370         begin = 5;
1371         end = 20;
1372         sql << "select i from t", soci::into(v, begin, end);
1373 
1374         CHECK(end == 8);
1375         for (std::size_t i = 0; i != 5; ++i)
1376         {
1377             CHECK(v[i].get() == -1);
1378         }
1379         CHECK(v[5].get() == 30);
1380         CHECK(v[6].get() == 40);
1381         CHECK(v[7].get() == 50);
1382         for (std::size_t i = end; i != 20; ++i)
1383         {
1384             CHECK(v[i].get() == -1);
1385         }
1386     }
1387 
1388     sql << "drop table t";
1389 }
1390 
1391 //
1392 // Support for soci Common Tests
1393 //
1394 
1395 struct table_creator_one : public table_creator_base
1396 {
1397     table_creator_one(soci::session & sql)
1398         : table_creator_base(sql)
1399     {
1400         sql << "create table soci_test(id number(10,0), val number(4,0), c char, "
1401                  "str varchar2(20), sh number, ul number, d number, "
1402                  "num76 numeric(7,6), "
1403                  "tm date, i1 number, i2 number, i3 number, name varchar2(20))";
1404     }
1405 };
1406 
1407 struct table_creator_two : public table_creator_base
1408 {
1409     table_creator_two(soci::session & sql)
1410         : table_creator_base(sql)
1411     {
1412         sql  << "create table soci_test(num_float number, num_int numeric(4,0),"
1413                     " name varchar2(20), sometime date, chr char)";
1414     }
1415 };
1416 
1417 struct table_creator_three : public table_creator_base
1418 {
1419     table_creator_three(soci::session & sql)
1420         : table_creator_base(sql)
1421     {
1422         sql << "create table soci_test(name varchar2(100) not null, "
1423             "phone varchar2(15))";
1424     }
1425 };
1426 
1427 struct table_creator_four : public table_creator_base
1428 {
1429     table_creator_four(soci::session & sql)
1430         : table_creator_base(sql)
1431     {
1432         sql << "create table soci_test(val number)";
1433     }
1434 };
1435 
1436 struct table_creator_for_xml : table_creator_base
1437 {
1438     table_creator_for_xml(soci::session& sql)
1439         : table_creator_base(sql)
1440     {
1441         sql << "create table soci_test(id integer, x xmltype)";
1442     }
1443 };
1444 
1445 struct table_creator_for_clob : table_creator_base
1446 {
1447     table_creator_for_clob(soci::session& sql)
1448         : table_creator_base(sql)
1449     {
1450         sql << "create table soci_test(id integer, s clob)";
1451     }
1452 };
1453 
1454 class test_context :public test_context_base
1455 {
1456 public:
1457     test_context(backend_factory const &backEnd,
1458                 std::string const &connectString)
1459         : test_context_base(backEnd, connectString) {}
1460 
1461     table_creator_base* table_creator_1(soci::session& s) const override
1462     {
1463         return new table_creator_one(s);
1464     }
1465 
1466     table_creator_base* table_creator_2(soci::session& s) const override
1467     {
1468         return new table_creator_two(s);
1469     }
1470 
1471     table_creator_base* table_creator_3(soci::session& s) const override
1472     {
1473         return new table_creator_three(s);
1474     }
1475 
1476     table_creator_base* table_creator_4(soci::session& s) const override
1477     {
1478         return new table_creator_four(s);
1479     }
1480 
1481     table_creator_base* table_creator_clob(soci::session& s) const override
1482     {
1483         return new table_creator_for_clob(s);
1484     }
1485 
1486     table_creator_base* table_creator_xml(soci::session& s) const override
1487     {
1488         return new table_creator_for_xml(s);
1489     }
1490 
1491     std::string to_xml(std::string const& x) const override
1492     {
1493         return "xmltype(" + x + ")";
1494     }
1495 
1496     std::string from_xml(std::string const& x) const override
1497     {
1498         // Notice that using just x.getCLOBVal() doesn't work, only
1499         // table.x.getCLOBVal() or (x).getCLOBVal(), as used here, does.
1500         return "(" + x + ").getCLOBVal()";
1501     }
1502 
1503     bool has_real_xml_support() const override
1504     {
1505         return true;
1506     }
1507 
1508     bool treats_empty_strings_as_null() const override
1509     {
1510         return true;
1511     }
1512 
1513     std::string to_date_time(std::string const &datdt_string) const override
1514     {
1515         return "to_date('" + datdt_string + "', 'YYYY-MM-DD HH24:MI:SS')";
1516     }
1517 
1518     std::string sql_length(std::string const& s) const override
1519     {
1520         // Oracle treats empty strings as NULLs, but we want to return the
1521         // length of 0 for them for consistency with the other backends, so use
1522         // nvl() explicitly to achieve this.
1523         return "nvl(length(" + s + "), 0)";
1524     }
1525 };
1526 
1527 int main(int argc, char** argv)
1528 {
1529 #ifdef _MSC_VER
1530     // Redirect errors, unrecoverable problems, and assert() failures to STDERR,
1531     // instead of debug message window.
1532     // This hack is required to run assert()-driven tests by Buildbot.
1533     // NOTE: Comment this 2 lines for debugging with Visual C++ debugger to catch assertions inside.
1534     _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
1535     _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
1536 #endif //_MSC_VER
1537 
1538     if (argc >= 2)
1539     {
1540         connectString = argv[1];
1541 
1542         // Replace the connect string with the process name to ensure that
1543         // CATCH uses the correct name in its messages.
1544         argv[1] = argv[0];
1545 
1546         argc--;
1547         argv++;
1548     }
1549     else
1550     {
1551         std::cout << "usage: " << argv[0]
1552             << " connectstring [test-arguments...]\n"
1553             << "example: " << argv[0]
1554             << " \'service=orcl user=scott password=tiger\'\n";
1555         std::exit(1);
1556     }
1557 
1558     if (!std::getenv("ORACLE_HOME"))
1559     {
1560         std::cerr << "ORACLE_HOME environment variable must be defined for Oracle tests.\n";
1561         std::exit(1);
1562     }
1563 
1564     test_context tc(backEnd, connectString);
1565 
1566     return Catch::Session().run(argc, argv);
1567 }