00001 // 00002 // // Copyright (C) 2004-2007 Maciej Sobczak, Stephen Hutton 00003 // Distributed under the Boost Software License, Version 1.0. 00004 // (See accompanying file LICENSE_1_0.txt or copy at 00005 // http://www.boost.org/LICENSE_1_0.txt) 00006 // 00007 00008 // 00009 #if defined(SOCI_HEADERS_BURIED) 00010 # include <soci/core/soci.h> 00011 # include <soci/backends/oracle/soci-oracle.h> 00012 # include <soci/core/test/common-tests.h> 00013 #else 00014 # include <soci.h> 00015 # include <soci-oracle.h> 00016 # include <test/common-tests.h> 00017 #endif 00018 // 00019 #include <iostream> 00020 #include <string> 00021 #include <cassert> 00022 #include <ctime> 00023 #include <cstring> 00024 00025 using namespace soci; 00026 using namespace soci::tests; 00027 00028 std::string connectString; 00029 backend_factory const &backEnd = oracle; 00030 00031 // Extra tests for date/time 00032 void test1() 00033 { 00034 session sql(backEnd, connectString); 00035 00036 { 00037 std::time_t now = std::time(NULL); 00038 std::tm t1, t2; 00039 t2 = *std::localtime(&now); 00040 00041 sql << "select t from (select :t as t from dual)", 00042 into(t1), use(t2); 00043 assert(memcmp(&t1, &t2, sizeof(std::tm)) == 0); 00044 00045 // make sure the date is stored properly in Oracle 00046 char buf[25]; 00047 strftime(buf, sizeof(buf), "%m-%d-%Y %H:%M:%S", &t2); 00048 00049 std::string t_out; 00050 std::string format("MM-DD-YYYY HH24:MI:SS"); 00051 sql << "select to_char(t, :format) from (select :t as t from dual)", 00052 into(t_out), use(format), use(t2); 00053 00054 assert(t_out == std::string(buf)); 00055 } 00056 00057 { 00058 // date and time - before year 2000 00059 std::time_t then = std::time(NULL) - 17*365*24*60*60; 00060 std::tm t1, t2; 00061 t2 = *std::localtime(&then); 00062 00063 sql << "select t from (select :t as t from dual)", 00064 into(t1), use(t2); 00065 00066 assert(memcmp(&t1, &t2, sizeof(std::tm)) == 0); 00067 00068 // make sure the date is stored properly in Oracle 00069 char buf[25]; 00070 strftime(buf, sizeof(buf), "%m-%d-%Y %H:%M:%S", &t2); 00071 00072 std::string t_out; 00073 std::string format("MM-DD-YYYY HH24:MI:SS"); 00074 sql << "select to_char(t, :format) from (select :t as t from dual)", 00075 into(t_out), use(format), use(t2); 00076 00077 assert(t_out == std::string(buf)); 00078 } 00079 00080 std::cout << "test 1 passed" << std::endl; 00081 } 00082 00083 // explicit calls test 00084 void test2() 00085 { 00086 session sql(backEnd, connectString); 00087 00088 statement st(sql); 00089 st.alloc(); 00090 int i = 0; 00091 st.exchange(into(i)); 00092 st.prepare("select 7 from dual"); 00093 st.define_and_bind(); 00094 st.execute(1); 00095 assert(i == 7); 00096 00097 std::cout << "test 2 passed" << std::endl; 00098 } 00099 00100 // DDL + blob test 00101 00102 struct blob_table_creator : public table_creator_base 00103 { 00104 blob_table_creator(session & sql) 00105 : table_creator_base(sql) 00106 { 00107 sql << 00108 "create table soci_test (" 00109 " id number(10) not null," 00110 " img blob" 00111 ")"; 00112 } 00113 }; 00114 00115 void test3() 00116 { 00117 session sql(backEnd, connectString); 00118 00119 blob_table_creator tableCreator(sql); 00120 00121 char buf[] = "abcdefghijklmnopqrstuvwxyz"; 00122 sql << "insert into soci_test (id, img) values (7, empty_blob())"; 00123 00124 { 00125 blob b(sql); 00126 00127 oracle_session_backend *sessionBackEnd 00128 = static_cast<oracle_session_backend *>(sql.get_backend()); 00129 00130 oracle_blob_backend *blobBackEnd 00131 = static_cast<oracle_blob_backend *>(b.get_backend()); 00132 00133 OCILobDisableBuffering(sessionBackEnd->svchp_, 00134 sessionBackEnd->errhp_, blobBackEnd->lobp_); 00135 00136 sql << "select img from soci_test where id = 7", into(b); 00137 assert(b.get_len() == 0); 00138 00139 // note: blob offsets start from 1 00140 b.write(1, buf, sizeof(buf)); 00141 assert(b.get_len() == sizeof(buf)); 00142 b.trim(10); 00143 assert(b.get_len() == 10); 00144 00145 // append does not work (Oracle bug #886191 ?) 00146 //b.append(buf, sizeof(buf)); 00147 //assert(b.get_len() == sizeof(buf) + 10); 00148 sql.commit(); 00149 } 00150 00151 { 00152 blob b(sql); 00153 sql << "select img from soci_test where id = 7", into(b); 00154 //assert(b.get_len() == sizeof(buf) + 10); 00155 assert(b.get_len() == 10); 00156 char buf2[100]; 00157 b.read(1, buf2, 10); 00158 assert(strncmp(buf2, "abcdefghij", 10) == 0); 00159 } 00160 00161 std::cout << "test 3 passed" << std::endl; 00162 } 00163 00164 // nested statement test 00165 // (the same syntax is used for output cursors in PL/SQL) 00166 00167 struct basic_table_creator : public table_creator_base 00168 { 00169 basic_table_creator(session & sql) 00170 : table_creator_base(sql) 00171 { 00172 sql << 00173 "create table soci_test (" 00174 " id number(5) not null," 00175 " name varchar2(100)," 00176 " code number(5)" 00177 ")"; 00178 } 00179 }; 00180 00181 void test4() 00182 { 00183 session sql(backEnd, connectString); 00184 basic_table_creator tableCreator(sql); 00185 00186 int id; 00187 std::string name; 00188 { 00189 statement st1 = (sql.prepare << 00190 "insert into soci_test (id, name) values (:id, :name)", 00191 use(id), use(name)); 00192 00193 id = 1; name = "John"; st1.execute(1); 00194 id = 2; name = "Anna"; st1.execute(1); 00195 id = 3; name = "Mike"; st1.execute(1); 00196 } 00197 00198 statement stInner(sql); 00199 statement stOuter = (sql.prepare << 00200 "select cursor(select name from soci_test order by id)" 00201 " from soci_test where id = 1", 00202 into(stInner)); 00203 stInner.exchange(into(name)); 00204 stOuter.execute(); 00205 stOuter.fetch(); 00206 00207 std::vector<std::string> names; 00208 while (stInner.fetch()) { names.push_back(name); } 00209 00210 assert(names.size() == 3); 00211 assert(names[0] == "John"); 00212 assert(names[1] == "Anna"); 00213 assert(names[2] == "Mike"); 00214 00215 std::cout << "test 4 passed" << std::endl; 00216 } 00217 00218 00219 // ROWID test 00220 void test5() 00221 { 00222 session sql(backEnd, connectString); 00223 basic_table_creator tableCreator(sql); 00224 00225 sql << "insert into soci_test(id, name) values(7, \'John\')"; 00226 00227 rowid rid(sql); 00228 sql << "select rowid from soci_test where id = 7", into(rid); 00229 00230 int id; 00231 std::string name; 00232 sql << "select id, name from soci_test where rowid = :rid", 00233 into(id), into(name), use(rid); 00234 00235 assert(id == 7); 00236 assert(name == "John"); 00237 00238 std::cout << "test 5 passed" << std::endl; 00239 } 00240 00241 // Stored procedures 00242 struct procedure_creator : procedure_creator_base 00243 { 00244 procedure_creator(session & sql) 00245 : procedure_creator_base(sql) 00246 { 00247 sql << 00248 "create or replace procedure soci_test(output out varchar2," 00249 "input in varchar2) as " 00250 "begin output := input; end;"; 00251 } 00252 }; 00253 00254 void test6() 00255 { 00256 { 00257 session sql(backEnd, connectString); 00258 procedure_creator procedure_creator(sql); 00259 00260 std::string in("my message"); 00261 std::string out; 00262 statement st = (sql.prepare << 00263 "begin soci_test(:output, :input); end;", 00264 use(out, "output"), 00265 use(in, "input")); 00266 st.execute(1); 00267 assert(out == in); 00268 00269 // explicit procedure syntax 00270 { 00271 std::string in("my message2"); 00272 std::string out; 00273 procedure proc = (sql.prepare << 00274 "soci_test(:output, :input)", 00275 use(out, "output"), use(in, "input")); 00276 proc.execute(1); 00277 assert(out == in); 00278 } 00279 } 00280 00281 std::cout << "test 6 passed" << std::endl; 00282 } 00283 00284 // bind into user-defined objects 00285 struct string_holder 00286 { 00287 string_holder() {} 00288 string_holder(const char* s) : s_(s) {} 00289 string_holder(std::string s) : s_(s) {} 00290 std::string get() const { return s_; } 00291 private: 00292 std::string s_; 00293 }; 00294 00295 namespace soci 00296 { 00297 template <> 00298 struct type_conversion<string_holder> 00299 { 00300 typedef std::string base_type; 00301 static void from_base(const std::string &s, indicator /* ind */, 00302 string_holder &sh) 00303 { 00304 sh = string_holder(s); 00305 } 00306 00307 static void to_base(const string_holder &sh, std::string &s, indicator &ind) 00308 { 00309 s = sh.get(); 00310 ind = i_ok; 00311 } 00312 }; 00313 } 00314 00315 struct in_out_procedure_creator : public procedure_creator_base 00316 { 00317 in_out_procedure_creator(session & sql) 00318 : procedure_creator_base(sql) 00319 { 00320 sql << "create or replace procedure soci_test(s in out varchar2)" 00321 " as begin s := s || s; end;"; 00322 } 00323 }; 00324 00325 struct returns_null_procedure_creator : public procedure_creator_base 00326 { 00327 returns_null_procedure_creator(session & sql) 00328 : procedure_creator_base(sql) 00329 { 00330 sql << "create or replace procedure soci_test(s in out varchar2)" 00331 " as begin s := NULL; end;"; 00332 } 00333 }; 00334 00335 void test7() 00336 { 00337 session sql(backEnd, connectString); 00338 { 00339 basic_table_creator tableCreator(sql); 00340 00341 int id(1); 00342 string_holder in("my string"); 00343 sql << "insert into soci_test(id, name) values(:id, :name)", use(id), use(in); 00344 00345 string_holder out; 00346 sql << "select name from soci_test", into(out); 00347 assert(out.get() == "my string"); 00348 00349 row r; 00350 sql << "select * from soci_test", into(r); 00351 string_holder dynamicOut = r.get<string_holder>(1); 00352 assert(dynamicOut.get() == "my string"); 00353 } 00354 00355 // test procedure with user-defined type as in-out parameter 00356 { 00357 in_out_procedure_creator procedureCreator(sql); 00358 00359 string_holder sh("test"); 00360 procedure proc = (sql.prepare << "soci_test(:s)", use(sh)); 00361 proc.execute(1); 00362 assert(sh.get() == "testtest"); 00363 } 00364 00365 // test procedure which returns null 00366 { 00367 returns_null_procedure_creator procedureCreator(sql); 00368 00369 string_holder sh; 00370 indicator ind = i_ok; 00371 procedure proc = (sql.prepare << "soci_test(:s)", use(sh, ind)); 00372 proc.execute(1); 00373 assert(ind == i_null); 00374 } 00375 00376 std::cout << "test 7 passed" << std::endl; 00377 } 00378 00379 // test bulk insert features 00380 void test8() 00381 { 00382 session sql(backEnd, connectString); 00383 00384 basic_table_creator tableCreator(sql); 00385 00386 // verify exception is thrown if vectors of unequal size are passed in 00387 { 00388 std::vector<int> ids; 00389 ids.push_back(1); 00390 ids.push_back(2); 00391 std::vector<int> codes; 00392 codes.push_back(1); 00393 std::string error; 00394 00395 try 00396 { 00397 sql << "insert into soci_test(id,code) values(:id,:code)", 00398 use(ids), use(codes); 00399 } 00400 catch (soci_error const &e) 00401 { 00402 error = e.what(); 00403 } 00404 assert(error.find("Bind variable size mismatch") 00405 != std::string::npos); 00406 00407 try 00408 { 00409 sql << "select from soci_test", into(ids), into(codes); 00410 } 00411 catch (std::exception const &e) 00412 { 00413 error = e.what(); 00414 } 00415 assert(error.find("Bind variable size mismatch") 00416 != std::string::npos); 00417 } 00418 00419 // verify partial insert occurs when one of the records is bad 00420 { 00421 std::vector<int> ids; 00422 ids.push_back(100); 00423 ids.push_back(1000000); // too big for column 00424 00425 std::string error; 00426 try 00427 { 00428 sql << "insert into soci_test (id) values(:id)", use(ids, "id"); 00429 } 00430 catch (soci_error const &e) 00431 { 00432 error = e.what(); 00433 //TODO e could be made to tell which row(s) failed 00434 } 00435 sql.commit(); 00436 assert(error.find("ORA-01438") != std::string::npos); 00437 int count(7); 00438 sql << "select count(*) from soci_test", into(count); 00439 assert(count == 1); 00440 sql << "delete from soci_test"; 00441 } 00442 00443 // test insert 00444 { 00445 std::vector<int> ids; 00446 for (int i = 0; i != 3; ++i) 00447 { 00448 ids.push_back(i+10); 00449 } 00450 00451 statement st = (sql.prepare << "insert into soci_test(id) values(:id)", 00452 use(ids)); 00453 st.execute(1); 00454 int count; 00455 sql << "select count(*) from soci_test", into(count); 00456 assert(count == 3); 00457 } 00458 00459 //verify an exception is thrown if into vector is zero length 00460 { 00461 std::vector<int> ids; 00462 bool caught(false); 00463 try 00464 { 00465 sql << "select id from soci_test", into(ids); 00466 } 00467 catch (soci_error const &) 00468 { 00469 caught = true; 00470 } 00471 assert(caught); 00472 } 00473 00474 // verify an exception is thrown if use vector is zero length 00475 { 00476 std::vector<int> ids; 00477 bool caught(false); 00478 try 00479 { 00480 sql << "insert into soci_test(id) values(:id)", use(ids); 00481 } 00482 catch (soci_error const &) 00483 { 00484 caught = true; 00485 } 00486 assert(caught); 00487 } 00488 00489 // test "no data" condition 00490 { 00491 std::vector<indicator> inds(3); 00492 std::vector<int> ids_out(3); 00493 statement st = (sql.prepare << "select id from soci_test where 1=0", 00494 into(ids_out, inds)); 00495 00496 // false return value means "no data" 00497 assert(st.execute(1) == false); 00498 00499 // that's it - nothing else is guaranteed 00500 // and nothing else is to be tested here 00501 } 00502 00503 // test NULL indicators 00504 { 00505 std::vector<int> ids(3); 00506 sql << "select id from soci_test", into(ids); 00507 00508 std::vector<indicator> inds_in; 00509 inds_in.push_back(i_ok); 00510 inds_in.push_back(i_null); 00511 inds_in.push_back(i_ok); 00512 00513 std::vector<int> new_codes; 00514 new_codes.push_back(10); 00515 new_codes.push_back(11); 00516 new_codes.push_back(10); 00517 00518 sql << "update soci_test set code = :code where id = :id", 00519 use(new_codes, inds_in), use(ids); 00520 00521 std::vector<indicator> inds_out(3); 00522 std::vector<int> codes(3); 00523 00524 sql << "select code from soci_test", into(codes, inds_out); 00525 assert(codes.size() == 3 && inds_out.size() == 3); 00526 assert(codes[0] == 10 && codes[2] == 10); 00527 assert(inds_out[0] == i_ok && inds_out[1] == i_null 00528 && inds_out[2] == i_ok); 00529 } 00530 00531 // verify an exception is thrown if null is selected 00532 // and no indicator was provided 00533 { 00534 std::string msg; 00535 std::vector<int> intos(3); 00536 try 00537 { 00538 sql << "select code from soci_test", into(intos); 00539 } 00540 catch (soci_error const &e) 00541 { 00542 msg = e.what(); 00543 } 00544 assert(msg == "Null value fetched and no indicator defined." ); 00545 } 00546 00547 // test basic select 00548 { 00549 const size_t sz = 3; 00550 std::vector<indicator> inds(sz); 00551 std::vector<int> ids_out(sz); 00552 statement st = (sql.prepare << "select id from soci_test", 00553 into(ids_out, inds)); 00554 const bool gotData = st.execute(true); 00555 assert(gotData); 00556 assert(ids_out.size() == sz); 00557 assert(ids_out[0] == 10); 00558 assert(ids_out[2] == 12); 00559 assert(inds.size() == 3 && inds[0] == i_ok 00560 && inds[1] == i_ok && inds[2] == i_ok); 00561 } 00562 00563 // verify execute(0) 00564 { 00565 std::vector<int> ids_out(2); 00566 statement st = (sql.prepare << "select id from soci_test", 00567 into(ids_out)); 00568 00569 st.execute(); 00570 assert(ids_out.size() == 2); 00571 bool gotData = st.fetch(); 00572 assert(gotData); 00573 assert(ids_out.size() == 2 && ids_out[0] == 10 && ids_out[1] == 11); 00574 gotData = st.fetch(); 00575 assert(gotData); 00576 assert(ids_out.size() == 1 && ids_out[0] == 12); 00577 gotData = st.fetch(); 00578 assert(gotData == false); 00579 } 00580 00581 // verify resizing happens if vector is larger 00582 // than number of rows returned 00583 { 00584 std::vector<int> ids_out(4); // one too many 00585 statement st2 = (sql.prepare << "select id from soci_test", 00586 into(ids_out)); 00587 bool gotData = st2.execute(true); 00588 assert(gotData); 00589 assert(ids_out.size() == 3); 00590 assert(ids_out[0] == 10); 00591 assert(ids_out[2] == 12); 00592 } 00593 00594 // verify resizing happens properly during fetch() 00595 { 00596 std::vector<int> more; 00597 more.push_back(13); 00598 more.push_back(14); 00599 sql << "insert into soci_test(id) values(:id)", use(more); 00600 00601 std::vector<int> ids(2); 00602 statement st3 = (sql.prepare << "select id from soci_test", into(ids)); 00603 bool gotData = st3.execute(true); 00604 assert(gotData); 00605 assert(ids[0] == 10); 00606 assert(ids[1] == 11); 00607 00608 gotData = st3.fetch(); 00609 assert(gotData); 00610 assert(ids[0] == 12); 00611 assert(ids[1] == 13); 00612 00613 gotData = st3.fetch(); 00614 assert(gotData); 00615 assert(ids.size() == 1); 00616 assert(ids[0] == 14); 00617 00618 gotData = st3.fetch(); 00619 assert(gotData == false); 00620 } 00621 00622 std::cout << "test 8 passed" << std::endl; 00623 } 00624 00625 // more tests for bulk fetch 00626 void test9() 00627 { 00628 session sql(backEnd, connectString); 00629 00630 basic_table_creator tableCreator(sql); 00631 00632 std::vector<int> in; 00633 for (int i = 1; i <= 10; ++i) 00634 { 00635 in.push_back(i); 00636 } 00637 00638 sql << "insert into soci_test (id) values(:id)", use(in); 00639 00640 int count(0); 00641 sql << "select count(*) from soci_test", into(count); 00642 assert(count == 10); 00643 00644 // verify that the exception is thrown when trying to resize 00645 // the output vector to the size that is bigger than that 00646 // at the time of binding 00647 { 00648 std::vector<int> out(4); 00649 statement st = (sql.prepare << 00650 "select id from soci_test", into(out)); 00651 00652 st.execute(); 00653 00654 st.fetch(); 00655 assert(out.size() == 4); 00656 assert(out[0] == 1); 00657 assert(out[1] == 2); 00658 assert(out[2] == 3); 00659 assert(out[3] == 4); 00660 out.resize(5); // this should be detected as error 00661 try 00662 { 00663 st.fetch(); 00664 assert(false); // should never reach here 00665 } 00666 catch (soci_error const &e) 00667 { 00668 assert(std::string(e.what()) == 00669 "Increasing the size of the output vector is not supported."); 00670 } 00671 } 00672 00673 // on the other hand, downsizing is OK 00674 { 00675 std::vector<int> out(4); 00676 statement st = (sql.prepare << 00677 "select id from soci_test", into(out)); 00678 00679 st.execute(); 00680 00681 st.fetch(); 00682 assert(out.size() == 4); 00683 assert(out[0] == 1); 00684 assert(out[1] == 2); 00685 assert(out[2] == 3); 00686 assert(out[3] == 4); 00687 out.resize(3); // ok 00688 st.fetch(); 00689 assert(out.size() == 3); 00690 assert(out[0] == 5); 00691 assert(out[1] == 6); 00692 assert(out[2] == 7); 00693 out.resize(4); // ok, not bigger than initially 00694 st.fetch(); 00695 assert(out.size() == 3); // downsized because of end of data 00696 assert(out[0] == 8); 00697 assert(out[1] == 9); 00698 assert(out[2] == 10); 00699 bool gotData = st.fetch(); 00700 assert(gotData == false); // end of data 00701 } 00702 00703 std::cout << "test 9 passed" << std::endl; 00704 } 00705 00706 struct person 00707 { 00708 int id; 00709 std::string firstName; 00710 string_holder lastName; //test mapping of type_conversion-based types 00711 std::string gender; 00712 }; 00713 00714 // Object-Relational Mapping 00715 // Note: Use the values class as shown below in type_conversions 00716 // to achieve object relational mapping. The values class should 00717 // not be used directly in any other fashion. 00718 namespace soci 00719 { 00720 // name-based conversion 00721 template<> struct type_conversion<person> 00722 { 00723 typedef values base_type; 00724 00725 static void from_base(values const &v, indicator /* ind */, person &p) 00726 { 00727 // ignoring possibility that the whole object might be NULL 00728 00729 p.id = v.get<int>("ID"); 00730 p.firstName = v.get<std::string>("FIRST_NAME"); 00731 p.lastName = v.get<string_holder>("LAST_NAME"); 00732 p.gender = v.get<std::string>("GENDER", "unknown"); 00733 } 00734 00735 static void to_base(person const & p, values & v, indicator & ind) 00736 { 00737 v.set("ID", p.id); 00738 v.set("FIRST_NAME", p.firstName); 00739 v.set("LAST_NAME", p.lastName); 00740 v.set("GENDER", p.gender, p.gender.empty() ? i_null : i_ok); 00741 ind = i_ok; 00742 } 00743 }; 00744 } 00745 00746 struct person_table_creator : public table_creator_base 00747 { 00748 person_table_creator(session & sql) 00749 : table_creator_base(sql) 00750 { 00751 sql << "create table soci_test(id numeric(5,0) NOT NULL," 00752 << " last_name varchar2(20), first_name varchar2(20), " 00753 " gender varchar2(10))"; 00754 } 00755 }; 00756 00757 struct times100_procedure_creator : public procedure_creator_base 00758 { 00759 times100_procedure_creator(session & sql) 00760 : procedure_creator_base(sql) 00761 { 00762 sql << "create or replace procedure soci_test(id in out number)" 00763 " as begin id := id * 100; end;"; 00764 } 00765 }; 00766 00767 void test10() 00768 { 00769 session sql(backEnd, connectString); 00770 00771 { 00772 person_table_creator tableCreator(sql); 00773 00774 person p; 00775 p.id = 1; 00776 p.lastName = "Smith"; 00777 p.firstName = "Pat"; 00778 sql << "insert into soci_test(id, first_name, last_name, gender) " 00779 << "values(:ID, :FIRST_NAME, :LAST_NAME, :GENDER)", use(p); 00780 00781 // p should be unchanged 00782 assert(p.id == 1); 00783 assert(p.firstName == "Pat"); 00784 assert(p.lastName.get() == "Smith"); 00785 00786 person p1; 00787 sql << "select * from soci_test", into(p1); 00788 assert(p1.id == 1); 00789 assert(p1.firstName + p1.lastName.get() == "PatSmith"); 00790 assert(p1.gender == "unknown"); 00791 00792 p.firstName = "Patricia"; 00793 sql << "update soci_test set first_name = :FIRST_NAME " 00794 "where id = :ID", use(p); 00795 00796 // p should be unchanged 00797 assert(p.id == 1); 00798 assert(p.firstName == "Patricia"); 00799 assert(p.lastName.get() == "Smith"); 00800 // Note: gender is now "unknown" because of the mapping, not "" 00801 assert(p.gender == "unknown"); 00802 00803 person p2; 00804 sql << "select * from soci_test", into(p2); 00805 assert(p2.id == 1); 00806 assert(p2.firstName + p2.lastName.get() == "PatriciaSmith"); 00807 00808 // insert a second row so we can test fetching 00809 person p3; 00810 p3.id = 2; 00811 p3.firstName = "Joe"; 00812 p3.lastName = "Smith"; 00813 sql << "insert into soci_test(id, first_name, last_name, gender) " 00814 << "values(:ID, :FIRST_NAME, :LAST_NAME, :GENDER)", use(p3); 00815 00816 person p4; 00817 statement st = (sql.prepare << "select * from soci_test order by id", 00818 into(p4)); 00819 00820 st.execute(); 00821 bool gotData = st.fetch(); 00822 assert(gotData); 00823 assert(p4.id == 1); 00824 assert(p4.firstName == "Patricia"); 00825 00826 gotData = st.fetch(); 00827 assert(gotData); 00828 assert(p4.id == 2); 00829 assert(p4.firstName == "Joe"); 00830 gotData = st.fetch(); 00831 assert(gotData == false); 00832 } 00833 00834 // test with stored procedure 00835 { 00836 times100_procedure_creator procedureCreator(sql); 00837 00838 person p; 00839 p.id = 1; 00840 p.firstName = "Pat"; 00841 p.lastName = "Smith"; 00842 procedure proc = (sql.prepare << "soci_test(:ID)", use(p)); 00843 proc.execute(1); 00844 assert(p.id == 100); 00845 assert(p.firstName == "Pat"); 00846 assert(p.lastName.get() == "Smith"); 00847 } 00848 00849 // test with stored procedure which returns null 00850 { 00851 returns_null_procedure_creator procedureCreator(sql); 00852 00853 std::string msg; 00854 person p; 00855 try 00856 { 00857 procedure proc = (sql.prepare << "soci_test(:FIRST_NAME)", 00858 use(p)); 00859 proc.execute(1); 00860 } 00861 catch (soci_error& e) 00862 { 00863 msg = e.what(); 00864 } 00865 assert(msg == "Null value not allowed for this type"); 00866 00867 procedure proc = (sql.prepare << "soci_test(:GENDER)", 00868 use(p)); 00869 proc.execute(1); 00870 assert(p.gender == "unknown"); 00871 00872 } 00873 std::cout << "test 10 passed" << std::endl; 00874 } 00875 00876 // Experimental support for position based O/R Mapping 00877 00878 // additional type for position-based test 00879 struct person2 00880 { 00881 int id; 00882 std::string firstName; 00883 std::string lastName; 00884 std::string gender; 00885 }; 00886 00887 // additional type for stream-like test 00888 struct person3 : person2 {}; 00889 00890 namespace soci 00891 { 00892 // position-based conversion 00893 template<> struct type_conversion<person2> 00894 { 00895 typedef values base_type; 00896 00897 static void from_base(values const &v, indicator /* ind */, person2 &p) 00898 { 00899 p.id = v.get<int>(0); 00900 p.firstName = v.get<std::string>(1); 00901 p.lastName = v.get<std::string>(2); 00902 p.gender = v.get<std::string>(3, "whoknows"); 00903 } 00904 00905 // What about the "to" part? Does it make any sense to have it? 00906 }; 00907 00908 // stream-like conversion 00909 template<> struct type_conversion<person3> 00910 { 00911 typedef values base_type; 00912 00913 static void from_base(values const &v, indicator /* ind */, person3 &p) 00914 { 00915 v >> p.id >> p.firstName >> p.lastName >> p.gender; 00916 } 00917 // TODO: The "to" part is certainly needed. 00918 }; 00919 } 00920 00921 void test11() 00922 { 00923 session sql(backEnd, connectString); 00924 00925 person_table_creator tableCreator(sql); 00926 00927 person p; 00928 p.id = 1; 00929 p.lastName = "Smith"; 00930 p.firstName = "Patricia"; 00931 sql << "insert into soci_test(id, first_name, last_name, gender) " 00932 << "values(:ID, :FIRST_NAME, :LAST_NAME, :GENDER)", use(p); 00933 00934 // test position-based conversion 00935 person2 p3; 00936 sql << "select id, first_name, last_name, gender from soci_test", into(p3); 00937 assert(p3.id == 1); 00938 assert(p3.firstName + p3.lastName == "PatriciaSmith"); 00939 assert(p3.gender == "whoknows"); 00940 00941 sql << "update soci_test set gender = 'F' where id = 1"; 00942 00943 // additional test for stream-like conversion 00944 person3 p4; 00945 sql << "select id, first_name, last_name, gender from soci_test", into(p4); 00946 assert(p4.id == 1); 00947 assert(p4.firstName + p4.lastName == "PatriciaSmith"); 00948 assert(p4.gender == "F"); 00949 00950 std::cout << "test 11 passed" << std::endl; 00951 } 00952 00953 // 00954 // Backwards compatibility - support use of large strings with 00955 // columns of type LONG 00957 struct long_table_creator : public table_creator_base 00958 { 00959 long_table_creator(session & sql) 00960 : table_creator_base(sql) 00961 { 00962 sql << "create table soci_test(l long)"; 00963 } 00964 }; 00965 00966 void test12() 00967 { 00968 session sql(backEnd, connectString); 00969 long_table_creator creator(sql); 00970 00971 const std::string::size_type max = 32768; 00972 std::string in(max, 'X'); 00973 00974 sql << "insert into soci_test values(:l)", use(in); 00975 00976 std::string out; 00977 sql << "select l from soci_test", into(out); 00978 00979 assert(out.size() == max); 00980 assert(in == out); 00981 00982 std::cout << "test 12 passed" << std::endl; 00983 } 00984 00985 // test for modifiable and const use elements 00986 void test13() 00987 { 00988 session sql(backEnd, connectString); 00989 00990 int i = 7; 00991 sql << "begin " 00992 "select 2 * :i into :i from dual; " 00993 "end;", use(i); 00994 assert(i == 14); 00995 00996 const int j = 7; 00997 try 00998 { 00999 sql << "begin " 01000 "select 2 * :i into :i from dual;" 01001 " end;", use(j); 01002 01003 assert(false); // should never get here 01004 } 01005 catch (soci_error const & e) 01006 { 01007 const std::string msg = e.what(); 01008 assert(msg == "Attempted modification of const use element"); 01009 } 01010 01011 std::cout << "test 13 passed" << std::endl; 01012 } 01013 01014 struct longlong_table_creator : table_creator_base 01015 { 01016 longlong_table_creator(session & sql) 01017 : table_creator_base(sql) 01018 { 01019 sql << "create table soci_test(val number(20))"; 01020 } 01021 }; 01022 01023 // long long test 01024 void test14() 01025 { 01026 { 01027 session sql(backEnd, connectString); 01028 01029 longlong_table_creator tableCreator(sql); 01030 01031 long long v1 = 1000000000000LL; 01032 assert(v1 / 1000000 == 1000000); 01033 01034 sql << "insert into soci_test(val) values(:val)", use(v1); 01035 01036 long long v2 = 0LL; 01037 sql << "select val from soci_test", into(v2); 01038 01039 assert(v2 == v1); 01040 } 01041 01042 // vector<long long> 01043 { 01044 session sql(backEnd, connectString); 01045 01046 longlong_table_creator tableCreator(sql); 01047 01048 std::vector<long long> v1; 01049 v1.push_back(1000000000000LL); 01050 v1.push_back(1000000000001LL); 01051 v1.push_back(1000000000002LL); 01052 v1.push_back(1000000000003LL); 01053 v1.push_back(1000000000004LL); 01054 01055 sql << "insert into soci_test(val) values(:val)", use(v1); 01056 01057 std::vector<long long> v2(10); 01058 sql << "select val from soci_test order by val desc", into(v2); 01059 01060 assert(v2.size() == 5); 01061 assert(v2[0] == 1000000000004LL); 01062 assert(v2[1] == 1000000000003LL); 01063 assert(v2[2] == 1000000000002LL); 01064 assert(v2[3] == 1000000000001LL); 01065 assert(v2[4] == 1000000000000LL); 01066 } 01067 01068 std::cout << "test 14 passed" << std::endl; 01069 } 01070 01071 // 01072 // Support for soci Common Tests 01073 // 01074 01075 struct table_creator_one : public table_creator_base 01076 { 01077 table_creator_one(session & sql) 01078 : table_creator_base(sql) 01079 { 01080 sql << "create table soci_test(id number(10,0), val number(4,0), c char, " 01081 "str varchar2(20), sh number, ul number, d number, " 01082 "tm date, i1 number, i2 number, i3 number, name varchar2(20))"; 01083 } 01084 }; 01085 01086 struct table_creator_two : public table_creator_base 01087 { 01088 table_creator_two(session & sql) 01089 : table_creator_base(sql) 01090 { 01091 sql << "create table soci_test(num_float number, num_int numeric(4,0)," 01092 " name varchar2(20), sometime date, chr char)"; 01093 } 01094 }; 01095 01096 struct table_creator_three : public table_creator_base 01097 { 01098 table_creator_three(session & sql) 01099 : table_creator_base(sql) 01100 { 01101 sql << "create table soci_test(name varchar2(100) not null, " 01102 "phone varchar2(15))"; 01103 } 01104 }; 01105 01106 class test_context :public test_context_base 01107 { 01108 public: 01109 test_context(backend_factory const &backEnd, 01110 std::string const &connectString) 01111 : test_context_base(backEnd, connectString) {} 01112 01113 table_creator_base* table_creator_1(session& s) const 01114 { 01115 return new table_creator_one(s); 01116 } 01117 01118 table_creator_base* table_creator_2(session& s) const 01119 { 01120 return new table_creator_two(s); 01121 } 01122 01123 table_creator_base* table_creator_3(session& s) const 01124 { 01125 return new table_creator_three(s); 01126 } 01127 01128 std::string to_date_time(std::string const &datdt_string) const 01129 { 01130 return "to_date('" + datdt_string + "', 'YYYY-MM-DD HH24:MI:SS')"; 01131 } 01132 }; 01133 01134 int main(int argc, char** argv) 01135 { 01136 #ifdef _MSC_VER 01137 // Redirect errors, unrecoverable problems, and assert() failures to STDERR, 01138 // instead of debug message window. 01139 // This hack is required to run asser()-driven tests by Buildbot. 01140 // NOTE: Comment this 2 lines for debugging with Visual C++ debugger to catch assertions inside. 01141 _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE); 01142 _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR); 01143 #endif //_MSC_VER 01144 01145 if (argc == 2) 01146 { 01147 connectString = argv[1]; 01148 } 01149 else 01150 { 01151 std::cout << "usage: " << argv[0] 01152 << " connectstring\n" 01153 << "example: " << argv[0] 01154 << " \'service=orcl user=scott password=tiger\'\n"; 01155 std::exit(1); 01156 } 01157 01158 try 01159 { 01160 test_context tc(backEnd, connectString); 01161 common_tests tests(tc); 01162 tests.run(); 01163 01164 std::cout << "\nsoci Oracle tests:\n\n"; 01165 test1(); 01166 test2(); 01167 test3(); 01168 test4(); 01169 test5(); 01170 test6(); 01171 test7(); 01172 test8(); 01173 test9(); 01174 test10(); 01175 test11(); 01176 test12(); 01177 test13(); 01178 test14(); 01179 01180 std::cout << "\nOK, all tests passed.\n\n"; 01181 } 01182 catch (std::exception const & e) 01183 { 01184 std::cout << e.what() << '\n'; 01185 } 01186 return 0; 01187 }
Generated on Sun Oct 3 2010 17:42:17 for EXTRAS-SOCI by Doxygen 1.7.1