Warning, file /sdk/codevis/thirdparty/soci/tests/firebird/test-firebird.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-2006 Maciej Sobczak, Stephen Hutton, Rafal Bobrowski 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 0009 #include "soci/soci.h" 0010 #include "soci/firebird/soci-firebird.h" 0011 #include "soci-compiler.h" 0012 #include "firebird/error-firebird.h" // soci::details::Firebird::throw_iscerror() 0013 #include "firebird/common.h" 0014 #include "common-tests.h" 0015 #include <iostream> 0016 #include <string> 0017 #include <ctime> 0018 #include <cstring> 0019 #include <cmath> 0020 0021 using namespace soci; 0022 0023 std::string connectString; 0024 soci::backend_factory const &backEnd = *factory_firebird(); 0025 0026 // fundamental tests - transactions in Firebird 0027 TEST_CASE("Firebird transactions", "[firebird][transaction]") 0028 { 0029 soci::session sql(backEnd, connectString); 0030 0031 // In Firebird transaction is always required and is started 0032 // automatically when session is opened. There is no need to 0033 // call session::begin(); it will do nothing if there is active 0034 // transaction. 0035 0036 // sql.begin(); 0037 0038 try 0039 { 0040 sql << "drop table test1"; 0041 } 0042 catch (soci_error const &) 0043 {} // ignore if error 0044 0045 sql << "create table test1 (id integer)"; 0046 0047 // After DDL statement transaction must be commited or changes 0048 // won't be visible to active transaction. 0049 sql.commit(); 0050 0051 // After commit or rollback, transaction must be started manually. 0052 sql.begin(); 0053 0054 sql << "insert into test1(id) values(5)"; 0055 sql << "drop table test1"; 0056 0057 // Transaction is automatically commited in session's destructor 0058 } 0059 0060 // character types 0061 TEST_CASE("Firebird char types", "[firebird][string]") 0062 { 0063 soci::session sql(backEnd, connectString); 0064 0065 try 0066 { 0067 sql << "drop table test2"; 0068 } 0069 catch (soci_error const &) 0070 {} // ignore if error 0071 0072 sql << "create table test2 (p1 char(10) character set none, p2 varchar(10) character set none)"; 0073 sql.commit(); 0074 0075 sql.begin(); 0076 0077 { 0078 char a('a'), b('b'), c1, c2; 0079 0080 sql << "insert into test2(p1,p2) values(?,?)", use(a), use(b); 0081 0082 sql << "select p1,p2 from test2", into(c1), into(c2); 0083 CHECK(c1 == 'a'); 0084 CHECK(c2 == 'b'); 0085 0086 sql << "delete from test2"; 0087 } 0088 0089 #if 0 // SOCI doesn't support binding into(char *, ...) anymore, use std::string 0090 { 0091 char msg[] = "Hello, Firebird!"; 0092 char buf1[100], buf2[100], buf3[100]; 0093 char *b1 = buf1, *b2 = buf2, *b3 = buf3; 0094 0095 strcpy(b1, msg); 0096 0097 sql << "insert into test2(p1, p2) values (?,?)", use(b1, 100), use(b1, 100); 0098 sql << "select p1, p2 from test2", into(b2, 100), into(b3, 100); 0099 0100 CHECK(!std::strcmp(buf2, buf3)); 0101 CHECK(!std::strcmp(buf2, "Hello, Fir")); 0102 0103 sql << "delete from test2"; 0104 } 0105 0106 { 0107 char msg[] = "Hello, Firebird!"; 0108 char buf1[100], buf2[100], buf3[100]; 0109 strcpy(buf1, msg); 0110 0111 sql << "insert into test2(p1, p2) values (?,?)", 0112 use(buf1), use(buf1); 0113 sql << "select p1, p2 from test2", into(buf2), into(buf3); 0114 0115 CHECK(!std::strcmp(buf2, buf3)); 0116 CHECK(!std::strcmp(buf2, "Hello, Fir")); 0117 0118 sql << "delete from test2"; 0119 } 0120 #endif 0121 0122 { 0123 // The test string is exactly 10 bytes long, i.e. same as column length. 0124 std::string b1("Hello, FB!"), b2, b3; 0125 0126 sql << "insert into test2(p1, p2) values (?,?)", use(b1), use(b1); 0127 sql << "select p1, p2 from test2", into(b2), into(b3); 0128 0129 CHECK(b2 == b3); 0130 CHECK(b2 == "Hello, FB!"); 0131 0132 sql << "delete from test2"; 0133 } 0134 0135 { 0136 // verify blank padding in CHAR fields 0137 // In Firebird, CHAR fields are always padded with whitespaces. 0138 char msg[] = "Hello"; 0139 sql << "insert into test2(p1) values(\'" << msg << "\')"; 0140 0141 char buf[20]; 0142 std::string buf_str; 0143 sql << "select p1 from test2", into(buf_str); 0144 std::strcpy(buf, buf_str.c_str()); 0145 0146 CHECK(std::strncmp(buf, msg, 5) == 0); 0147 // This test works only for charset none 0148 CHECK(std::strncmp(buf+5, " ", 5) == 0); 0149 0150 sql << "delete from test2"; 0151 } 0152 0153 sql << "drop table test2"; 0154 } 0155 0156 // date and time 0157 TEST_CASE("Firebird date and time", "[firebird][datetime]") 0158 { 0159 soci::session sql(backEnd, connectString); 0160 0161 try 0162 { 0163 sql << "drop table test3"; 0164 } 0165 catch (soci_error const &) 0166 {} // ignore if error 0167 0168 sql << "create table test3 (p1 timestamp, p2 date, p3 time)"; 0169 sql.commit(); 0170 0171 sql.begin(); 0172 0173 std::tm t1 = std::tm(); 0174 std::tm t2 = std::tm(); 0175 std::tm t3 = std::tm(); 0176 std::time_t now = std::time(NULL); 0177 std::tm t = *std::localtime(&now); 0178 sql << "insert into test3(p1, p2, p3) " 0179 << "values (?,?,?)", use(t), use(t), use(t); 0180 0181 sql << "select p1, p2, p3 from test3", into(t1), into(t2), into(t3); 0182 0183 // timestamp 0184 CHECK(t1.tm_year == t.tm_year); 0185 CHECK(t1.tm_mon == t.tm_mon); 0186 CHECK(t1.tm_mday == t.tm_mday); 0187 CHECK(t1.tm_hour == t.tm_hour); 0188 CHECK(t1.tm_min == t.tm_min); 0189 CHECK(t1.tm_sec == t.tm_sec); 0190 0191 // date 0192 CHECK(t2.tm_year == t.tm_year); 0193 CHECK(t2.tm_mon == t.tm_mon); 0194 CHECK(t2.tm_mday == t.tm_mday); 0195 CHECK(t2.tm_hour == 0); 0196 CHECK(t2.tm_min == 0); 0197 CHECK(t2.tm_sec == 0); 0198 0199 // time 0200 CHECK(t3.tm_year == 0); 0201 CHECK(t3.tm_mon == 0); 0202 CHECK(t3.tm_mday == 0); 0203 CHECK(t3.tm_hour == t.tm_hour); 0204 CHECK(t3.tm_min == t.tm_min); 0205 CHECK(t3.tm_sec == t.tm_sec); 0206 0207 sql << "drop table test3"; 0208 } 0209 0210 // floating points 0211 TEST_CASE("Firebird floating point", "[firebird][float]") 0212 { 0213 soci::session sql(backEnd, connectString); 0214 0215 try 0216 { 0217 sql << "drop table test4"; 0218 } 0219 catch (soci_error const &) 0220 {} // ignore if error 0221 0222 sql << "create table test4 (p1 numeric(8,2), " 0223 << "p2 decimal(14,8), p3 double precision, p4 integer)"; 0224 sql.commit(); 0225 0226 sql.begin(); 0227 0228 double d1 = 1234.23, d2 = 1e8, d3 = 1.0/1440.0, 0229 d4, d5, d6; 0230 0231 sql << "insert into test4(p1, p2, p3) values (?,?,?)", 0232 use(d1), use(d2), use(d3); 0233 0234 sql << "select p1, p2, p3 from test4", 0235 into(d4), into(d5), into(d6); 0236 0237 // The doubles should make the round trip unchanged, so use the exact 0238 // comparisons here. 0239 CHECK(tests::are_doubles_exactly_equal(d1, d4)); 0240 CHECK(tests::are_doubles_exactly_equal(d2, d5)); 0241 CHECK(tests::are_doubles_exactly_equal(d3, d6)); 0242 0243 // test negative doubles too 0244 sql << "delete from test4"; 0245 d1 = -d1; 0246 d2 = -d2; 0247 d3 = -d3; 0248 0249 sql << "insert into test4(p1, p2, p3) values (?,?,?)", 0250 use(d1), use(d2), use(d3); 0251 0252 sql << "select p1, p2, p3 from test4", 0253 into(d4), into(d5), into(d6); 0254 0255 CHECK(tests::are_doubles_exactly_equal(d1, d4)); 0256 CHECK(tests::are_doubles_exactly_equal(d2, d5)); 0257 CHECK(tests::are_doubles_exactly_equal(d3, d6)); 0258 0259 // verify an exception is thrown when fetching non-integral value 0260 // to integral variable 0261 try 0262 { 0263 int i; 0264 sql << "select p1 from test4", into(i); 0265 0266 // expecting error 0267 CHECK(false); 0268 } 0269 catch (soci_error const &e) 0270 { 0271 CHECK(e.get_error_message() == 0272 "Can't convert value with scale 2 to integral type"); 0273 } 0274 0275 // verify an exception is thrown when inserting non-integral value 0276 // to integral column 0277 try 0278 { 0279 sql << "insert into test4(p4) values(?)", use(d1); 0280 0281 // expecting error 0282 CHECK(false); 0283 } 0284 catch (soci_error const &e) 0285 { 0286 CHECK(e.get_error_message() == 0287 "Can't convert non-integral value to integral column type"); 0288 } 0289 0290 sql << "drop table test4"; 0291 } 0292 0293 // integer types and indicators 0294 TEST_CASE("Firebird integers", "[firebird][int]") 0295 { 0296 soci::session sql(backEnd, connectString); 0297 0298 { 0299 short sh(0); 0300 sql << "select 3 from rdb$database", into(sh); 0301 CHECK(sh == 3); 0302 } 0303 0304 { 0305 int i(0); 0306 sql << "select 5 from rdb$database", into(i); 0307 CHECK(i == 5); 0308 } 0309 0310 { 0311 unsigned long ul(0); 0312 sql << "select 7 from rdb$database", into(ul); 0313 CHECK(ul == 7); 0314 } 0315 0316 { 0317 // test indicators 0318 indicator ind; 0319 int i; 0320 0321 sql << "select 2 from rdb$database", into(i, ind); 0322 CHECK(ind == i_ok); 0323 0324 sql << "select NULL from rdb$database", into(i, ind); 0325 CHECK(ind == i_null); 0326 0327 #if 0 // SOCI doesn't support binding into(char *, ...) anymore, use std::string 0328 char buf[4]; 0329 sql << "select \'Hello\' from rdb$database", into(buf, ind); 0330 CHECK(ind == i_truncated); 0331 #endif 0332 0333 sql << "select 5 from rdb$database where 0 = 1", into(i, ind); 0334 CHECK(sql.got_data() == false); 0335 0336 try 0337 { 0338 // expect error 0339 sql << "select NULL from rdb$database", into(i); 0340 CHECK(false); 0341 } 0342 catch (soci_error const &e) 0343 { 0344 CHECK(e.get_error_message() == 0345 "Null value fetched and no indicator defined."); 0346 } 0347 0348 // expect no data 0349 sql << "select 5 from rdb$database where 0 = 1", into(i); 0350 CHECK(!sql.got_data()); 0351 } 0352 } 0353 0354 // repeated fetch and bulk operations for character types 0355 TEST_CASE("Firebird bulk operations", "[firebird][bulk]") 0356 { 0357 soci::session sql(backEnd, connectString); 0358 0359 try 0360 { 0361 sql << "drop table test6"; 0362 } 0363 catch (soci_error const &) 0364 {} // ignore if error 0365 0366 sql << "create table test6 (p1 char(10) character set none, p2 varchar(10) character set none)"; 0367 sql.commit(); 0368 0369 sql.begin(); 0370 0371 for (char c = 'a'; c <= 'z'; ++c) 0372 { 0373 sql << "insert into test6(p1, p2) values(?,?)", use(c), use(c); 0374 } 0375 0376 { 0377 char c, c1, c2; 0378 0379 statement st = (sql.prepare << 0380 "select p1,p2 from test6 order by p1", into(c1), into(c2)); 0381 0382 // Verify that fetch after re-executing the same statement works. 0383 for (int n = 0; n < 2; ++n) 0384 { 0385 st.execute(); 0386 0387 c='a'; 0388 while (st.fetch()) 0389 { 0390 CHECK(c == c1); 0391 CHECK(c == c2); 0392 ++c; 0393 } 0394 CHECK(c == 'z'+1); 0395 } 0396 } 0397 0398 { 0399 char c='a'; 0400 0401 std::vector<char> c1(10), c2(10); 0402 0403 statement st = (sql.prepare << 0404 "select p1,p2 from test6 order by p1", into(c1), into(c2)); 0405 0406 st.execute(); 0407 while (st.fetch()) 0408 { 0409 for (std::size_t i = 0; i != c1.size(); ++i) 0410 { 0411 CHECK(c == c1[i]); 0412 CHECK(c == c2[i]); 0413 ++c; 0414 } 0415 } 0416 CHECK(c == 'z' + 1); 0417 } 0418 0419 { 0420 // verify an exception is thrown when empty vector is used 0421 std::vector<char> vec; 0422 try 0423 { 0424 sql << "select p1 from test6", into(vec); 0425 CHECK(false); 0426 } 0427 catch (soci_error const &e) 0428 { 0429 CHECK(e.get_error_message() == 0430 "Vectors of size 0 are not allowed."); 0431 } 0432 } 0433 0434 sql << "delete from test6"; 0435 0436 // verifying std::string 0437 int const rowsToTest = 10; 0438 for (int i = 0; i != rowsToTest; ++i) 0439 { 0440 std::ostringstream ss; 0441 ss << "Hello_" << i; 0442 0443 std::string const &x = ss.str(); 0444 0445 sql << "insert into test6(p1, p2) values(\'" 0446 << x << "\', \'" << x << "\')"; 0447 } 0448 0449 int count; 0450 sql << "select count(*) from test6", into(count); 0451 CHECK(count == rowsToTest); 0452 0453 { 0454 int i = 0; 0455 std::string s1, s2; 0456 statement st = (sql.prepare << 0457 "select p1, p2 from test6 order by p1", into(s1), into(s2)); 0458 0459 st.execute(); 0460 while (st.fetch()) 0461 { 0462 std::ostringstream ss; 0463 ss << "Hello_" << i; 0464 std::string const &x = ss.str(); 0465 0466 // Note: CHAR fields are always padded with whitespaces 0467 ss << " "; 0468 CHECK(s1 == ss.str()); 0469 CHECK(s2 == x); 0470 ++i; 0471 } 0472 CHECK(i == rowsToTest); 0473 } 0474 0475 { 0476 int i = 0; 0477 0478 std::vector<std::string> s1(4), s2(4); 0479 statement st = (sql.prepare << 0480 "select p1, p2 from test6 order by p1", into(s1), into(s2)); 0481 st.execute(); 0482 while (st.fetch()) 0483 { 0484 for (std::size_t j = 0; j != s1.size(); ++j) 0485 { 0486 std::ostringstream ss; 0487 ss << "Hello_" << i; 0488 std::string const &x = ss.str(); 0489 0490 // Note: CHAR fields are always padded with whitespaces 0491 ss << " "; 0492 CHECK(ss.str() == s1[j]); 0493 CHECK(x == s2[j]); 0494 ++i; 0495 } 0496 } 0497 CHECK(i == rowsToTest); 0498 } 0499 0500 sql << "drop table test6"; 0501 } 0502 0503 // blob test 0504 TEST_CASE("Firebird blobs", "[firebird][blob]") 0505 { 0506 soci::session sql(backEnd, connectString); 0507 0508 try 0509 { 0510 sql << "drop table test7"; 0511 } 0512 catch (std::runtime_error &) 0513 {} // ignore if error 0514 0515 sql << "create table test7(id integer, img blob)"; 0516 sql.commit(); 0517 0518 sql.begin(); 0519 { 0520 // verify empty blob 0521 blob b(sql); 0522 indicator ind; 0523 0524 sql << "insert into test7(id, img) values(1,?)", use(b); 0525 sql << "select img from test7 where id = 1", into(b, ind); 0526 0527 CHECK(ind == i_ok); 0528 CHECK(b.get_len() == 0); 0529 0530 sql << "delete from test7"; 0531 } 0532 0533 { 0534 // create a new blob 0535 blob b(sql); 0536 0537 char str1[] = "Hello"; 0538 b.write_from_start(str1, strlen(str1)); 0539 0540 char str2[20]; 0541 std::size_t i = b.read_from_start(str2, 2, 3); 0542 str2[i] = '\0'; 0543 CHECK(str2[0] == 'l'); 0544 CHECK(str2[1] == 'o'); 0545 CHECK(str2[2] == '\0'); 0546 0547 char str3[] = ", Firebird!"; 0548 b.append(str3, strlen(str3)); 0549 0550 sql << "insert into test7(id, img) values(1,?)", use(b); 0551 } 0552 0553 { 0554 // read & update blob 0555 blob b(sql); 0556 0557 sql << "select img from test7 where id = 1", into(b); 0558 0559 std::vector<char> text(b.get_len()); 0560 b.read_from_start(&text[0], b.get_len()); 0561 CHECK(strncmp(&text[0], "Hello, Firebird!", b.get_len()) == 0); 0562 0563 char str1[] = "FIREBIRD"; 0564 b.write_from_start(str1, strlen(str1), 7); 0565 0566 // after modification blob must be written to database 0567 sql << "update test7 set img=? where id=1", use(b); 0568 } 0569 0570 { 0571 // read blob from database, modify and write to another record 0572 blob b(sql); 0573 0574 sql << "select img from test7 where id = 1", into(b); 0575 0576 std::vector<char> text(b.get_len()); 0577 b.read_from_start(&text[0], b.get_len()); 0578 0579 char str1[] = "HELLO"; 0580 b.write_from_start(str1, strlen(str1)); 0581 0582 b.read_from_start(&text[0], b.get_len()); 0583 CHECK(strncmp(&text[0], "HELLO, FIREBIRD!", b.get_len()) == 0); 0584 0585 b.trim(5); 0586 sql << "insert into test7(id, img) values(2,?)", use(b); 0587 } 0588 0589 { 0590 blob b(sql); 0591 statement st = (sql.prepare << "select img from test7", into(b)); 0592 0593 st.execute(); 0594 0595 st.fetch(); 0596 std::vector<char> text(b.get_len()); 0597 b.read_from_start(&text[0], b.get_len()); 0598 CHECK(strncmp(&text[0], "Hello, FIREBIRD!", b.get_len()) == 0); 0599 0600 st.fetch(); 0601 text.resize(b.get_len()); 0602 b.read_from_start(&text[0], b.get_len()); 0603 CHECK(strncmp(&text[0], "HELLO", b.get_len()) == 0); 0604 } 0605 0606 { 0607 // delete blob 0608 blob b(sql); 0609 indicator ind=i_null; 0610 sql << "update test7 set img=? where id = 1", use(b, ind); 0611 0612 sql << "select img from test7 where id = 2", into(b, ind); 0613 CHECK(ind==i_ok); 0614 0615 sql << "select img from test7 where id = 1", into(b, ind); 0616 CHECK(ind==i_null); 0617 } 0618 0619 { 0620 //create large blob 0621 const int blobSize = 65536; //max segment size is 65535(unsigned short) 0622 std::vector<char> data(blobSize); 0623 blob b(sql); 0624 b.write_from_start(data.data(), blobSize); 0625 sql << "insert into test7(id, img) values(3,?)", use(b); 0626 0627 //now read blob back from database and make sure it has correct content and size 0628 blob br(sql); 0629 sql << "select img from test7 where id = 3", into(br); 0630 std::vector<char> data2(br.get_len()); 0631 if(br.get_len()>0) 0632 br.read_from_start(data2.data(), br.get_len()); 0633 CHECK(data == data2); 0634 } 0635 0636 sql << "drop table test7"; 0637 } 0638 0639 // named parameters 0640 TEST_CASE("Firebird named parameters", "[firebird][named-params]") 0641 { 0642 soci::session sql(backEnd, connectString); 0643 0644 try 0645 { 0646 sql << "drop table test8"; 0647 } 0648 catch (std::runtime_error &) 0649 {} // ignore if error 0650 0651 sql << "create table test8(id1 integer, id2 integer)"; 0652 sql.commit(); 0653 0654 sql.begin(); 0655 0656 int j = 13, k = 4, i, m; 0657 sql << "insert into test8(id1, id2) values(:id1, :id2)", 0658 use(k, "id2"), use(j, "id1"); 0659 sql << "select id1, id2 from test8", into(i), into(m); 0660 CHECK(i == j); 0661 CHECK(m == k); 0662 0663 sql << "delete from test8"; 0664 0665 std::vector<int> in1(3), in2(3); 0666 in1[0] = 3; 0667 in1[1] = 2; 0668 in1[2] = 1; 0669 in2[0] = 4; 0670 in2[1] = 5; 0671 in2[2] = 6; 0672 0673 { 0674 statement st = (sql.prepare << 0675 "insert into test8(id1, id2) values(:id1, :id2)", 0676 use(k, "id2"), use(j, "id1")); 0677 0678 std::size_t s = in1.size(); 0679 for (std::size_t x = 0; x < s; ++x) 0680 { 0681 j = in1[x]; 0682 k = in2[x]; 0683 st.execute(); 0684 } 0685 } 0686 0687 { 0688 statement st = ( 0689 sql.prepare << "select id1, id2 from test8", into(i), into(m)); 0690 st.execute(); 0691 0692 std::size_t x(0); 0693 while (st.fetch()) 0694 { 0695 CHECK(i == in1[x]); 0696 CHECK(m == in2[x]); 0697 ++x; 0698 } 0699 } 0700 0701 sql << "delete from test8"; 0702 0703 // test vectors 0704 sql << "insert into test8(id1, id2) values(:id1, :id2)", 0705 use(in1, "id1"), use(in2, "id2"); 0706 0707 std::vector<int> out1(3), out2(3); 0708 0709 sql << "select id1, id2 from test8", into(out1), into(out2); 0710 std::size_t s = out1.size(); 0711 CHECK(s == 3); 0712 0713 for (std::size_t x = 0; x<s; ++x) 0714 { 0715 CHECK(out1[x] == in1[x]); 0716 CHECK(out2[x] == in2[x]); 0717 } 0718 0719 sql << "drop table test8"; 0720 } 0721 0722 // Dynamic binding to row objects 0723 TEST_CASE("Firebird dynamic binding", "[firebird][dynamic]") 0724 { 0725 soci::session sql(backEnd, connectString); 0726 0727 try 0728 { 0729 sql << "drop table test9"; 0730 } 0731 catch (std::runtime_error &) 0732 {} // ignore if error 0733 0734 sql << "create table test9(id integer, msg varchar(20), ntest numeric(10,2))"; 0735 sql.commit(); 0736 0737 sql.begin(); 0738 0739 { 0740 row r; 0741 sql << "select * from test9", into(r); 0742 CHECK(sql.got_data() == false); 0743 } 0744 0745 std::string msg("Hello"); 0746 int i(1); 0747 double d(3.14); 0748 indicator ind(i_ok); 0749 0750 { 0751 statement st((sql.prepare << "insert into test9(id, msg, ntest) " 0752 << "values(:id,:msg,:ntest)", 0753 use(i, "id"), use(msg, "msg"), use(d, ind, "ntest"))); 0754 0755 st.execute(1); 0756 0757 i = 2; 0758 msg = "Firebird"; 0759 ind = i_null; 0760 st.execute(1); 0761 } 0762 0763 row r; 0764 statement st = (sql.prepare << 0765 "select * from test9", into(r)); 0766 st.execute(1); 0767 0768 CHECK(r.size() == 3); 0769 0770 // get properties by position 0771 CHECK(r.get_properties(0).get_name() == "ID"); 0772 CHECK(r.get_properties(1).get_name() == "MSG"); 0773 CHECK(r.get_properties(2).get_name() == "NTEST"); 0774 0775 CHECK(r.get_properties(0).get_data_type() == dt_integer); 0776 CHECK(r.get_properties(1).get_data_type() == dt_string); 0777 CHECK(r.get_properties(2).get_data_type() == dt_double); 0778 0779 // get properties by name 0780 CHECK(r.get_properties("ID").get_name() == "ID"); 0781 CHECK(r.get_properties("MSG").get_name() == "MSG"); 0782 CHECK(r.get_properties("NTEST").get_name() == "NTEST"); 0783 0784 CHECK(r.get_properties("ID").get_data_type() == dt_integer); 0785 CHECK(r.get_properties("MSG").get_data_type() == dt_string); 0786 CHECK(r.get_properties("NTEST").get_data_type() == dt_double); 0787 0788 // get values by position 0789 CHECK(r.get<int>(0) == 1); 0790 CHECK(r.get<std::string>(1) == "Hello"); 0791 CHECK(tests::are_doubles_exactly_equal(r.get<double>(2), d)); 0792 0793 // get values by name 0794 CHECK(r.get<int>("ID") == 1); 0795 CHECK(r.get<std::string>("MSG") == "Hello"); 0796 CHECK(tests::are_doubles_exactly_equal(r.get<double>("NTEST"), d)); 0797 0798 st.fetch(); 0799 CHECK(r.get<int>(0) == 2); 0800 CHECK(r.get<std::string>("MSG") == "Firebird"); 0801 CHECK(r.get_indicator(2) == i_null); 0802 0803 // verify default values 0804 CHECK(tests::are_doubles_exactly_equal(r.get<double>("NTEST", 2), 2)); 0805 0806 CHECK_THROWS_AS(r.get<double>("NTEST"), soci_error); 0807 0808 // verify exception thrown on invalid get<> 0809 CHECK_THROWS_AS(r.get<std::string>(0), std::bad_cast); 0810 0811 sql << "drop table test9"; 0812 } 0813 0814 // stored procedures 0815 TEST_CASE("Firebird stored procedures", "[firebird][procedure]") 0816 { 0817 soci::session sql(backEnd, connectString); 0818 0819 try 0820 { 0821 sql << "drop procedure sp_test10"; 0822 } 0823 catch (std::runtime_error &) 0824 {} // ignore if error 0825 0826 try 0827 { 0828 sql << "drop procedure sp_test10a"; 0829 } 0830 catch (std::runtime_error &) 0831 {} // ignore if error 0832 0833 try 0834 { 0835 sql << "drop table test10"; 0836 } 0837 catch (std::runtime_error &) 0838 {} // ignore if error 0839 0840 sql << "create table test10(id integer, id2 integer)"; 0841 0842 sql << "create procedure sp_test10\n" 0843 << "returns (rid integer, rid2 integer)\n" 0844 << "as begin\n" 0845 << "for select id, id2 from test10 into rid, rid2 do begin\n" 0846 << "suspend;\n" 0847 << "end\n" 0848 << "end;\n"; 0849 0850 sql << "create procedure sp_test10a (pid integer, pid2 integer)\n" 0851 << "as begin\n" 0852 << "insert into test10(id, id2) values (:pid, :pid2);\n" 0853 << "end;\n"; 0854 0855 sql.commit(); 0856 0857 sql.begin(); 0858 0859 row r; 0860 int p1 = 3, p2 = 4; 0861 0862 // calling procedures that do not return values requires 0863 // 'execute procedure ...' statement 0864 sql << "execute procedure sp_test10a ?, ?", use(p1), use(p2); 0865 0866 // calling procedures that return values requires 0867 // 'select ... from ...' statement 0868 sql << "select * from sp_test10", into(r); 0869 0870 CHECK(r.get<int>(0) == p1); 0871 CHECK(r.get<int>(1) == p2); 0872 0873 sql << "delete from test10"; 0874 0875 p1 = 5; 0876 p2 = 6; 0877 { 0878 procedure proc = ( 0879 sql.prepare << "sp_test10a :p1, :p2", 0880 use(p2, "p2"), use(p1, "p1")); 0881 proc.execute(1); 0882 } 0883 0884 { 0885 row rw; 0886 procedure proc = (sql.prepare << "sp_test10", into(rw)); 0887 proc.execute(1); 0888 0889 CHECK(rw.get<int>(0) == p1); 0890 CHECK(rw.get<int>(1) == p2); 0891 } 0892 0893 sql << "delete from test10"; 0894 0895 // test vectors 0896 std::vector<int> in1(3), in2(3); 0897 in1[0] = 3; 0898 in1[1] = 2; 0899 in1[2] = 1; 0900 in2[0] = 4; 0901 in2[1] = 5; 0902 in2[2] = 6; 0903 0904 { 0905 procedure proc = ( 0906 sql.prepare << "sp_test10a :p1, :p2", 0907 use(in2, "p2"), use(in1, "p1")); 0908 proc.execute(1); 0909 } 0910 0911 { 0912 row rw; 0913 procedure proc = (sql.prepare << "sp_test10", into(rw)); 0914 0915 proc.execute(1); 0916 CHECK(rw.get<int>(0) == in1[0]); 0917 CHECK(rw.get<int>(1) == in2[0]); 0918 proc.fetch(); 0919 CHECK(rw.get<int>(0) == in1[1]); 0920 CHECK(rw.get<int>(1) == in2[1]); 0921 proc.fetch(); 0922 CHECK(rw.get<int>(0) == in1[2]); 0923 CHECK(rw.get<int>(1) == in2[2]); 0924 CHECK(proc.fetch() == false); 0925 } 0926 0927 { 0928 std::vector<int> out1(3), out2(3); 0929 procedure proc = (sql.prepare << "sp_test10", into(out1), into(out2)); 0930 proc.execute(1); 0931 0932 std::size_t s = out1.size(); 0933 CHECK(s == 3); 0934 0935 for (std::size_t x = 0; x < s; ++x) 0936 { 0937 CHECK(out1[x] == in1[x]); 0938 CHECK(out2[x] == in2[x]); 0939 } 0940 } 0941 0942 sql.rollback(); 0943 0944 sql.begin(); 0945 sql << "drop procedure sp_test10"; 0946 sql << "drop procedure sp_test10a"; 0947 sql << "drop table test10"; 0948 } 0949 0950 // direct access to Firebird using handles exposed by 0951 // soci::FirebirdStatmentBackend 0952 namespace soci 0953 { 0954 enum eRowCountType 0955 { 0956 eRowsSelected = isc_info_req_select_count, 0957 eRowsInserted = isc_info_req_insert_count, 0958 eRowsUpdated = isc_info_req_update_count, 0959 eRowsDeleted = isc_info_req_delete_count 0960 }; 0961 0962 // Returns number of rows afected by last statement 0963 // or -1 if there is no such counter available. 0964 long getRowCount(soci::statement & statement, eRowCountType type) 0965 { 0966 ISC_STATUS stat[20]; 0967 char cnt_req[2], cnt_info[128]; 0968 0969 cnt_req[0]=isc_info_sql_records; 0970 cnt_req[1]=isc_info_end; 0971 0972 firebird_statement_backend* statementBackEnd 0973 = static_cast<firebird_statement_backend*>(statement.get_backend()); 0974 0975 // Note: This is very poorly documented function. 0976 // It can extract number of rows returned by select statement, 0977 // but it appears that this is only number of rows prefetched by 0978 // client library, not total number of selected rows. 0979 if (isc_dsql_sql_info(stat, &statementBackEnd->stmtp_, sizeof(cnt_req), 0980 cnt_req, sizeof(cnt_info), cnt_info)) 0981 { 0982 soci::details::firebird::throw_iscerror(stat); 0983 } 0984 0985 long count = -1; 0986 char type_ = static_cast<char>(type); 0987 for (char *ptr = cnt_info + 3; *ptr != isc_info_end;) 0988 { 0989 char count_type = *ptr++; 0990 int m = isc_vax_integer(ptr, 2); 0991 ptr += 2; 0992 count = isc_vax_integer(ptr, static_cast<short>(m)); 0993 0994 if (count_type == type_) 0995 { 0996 // this is requested number 0997 break; 0998 } 0999 ptr += m; 1000 } 1001 1002 return count; 1003 } 1004 1005 } // namespace soci 1006 1007 TEST_CASE("Firebird direct API use", "[firebird][native]") 1008 { 1009 soci::session sql(backEnd, connectString); 1010 1011 try 1012 { 1013 sql << "drop table test11"; 1014 } 1015 catch (std::runtime_error &) 1016 {} // ignore if error 1017 1018 sql << "create table test11(id integer)"; 1019 sql.commit(); 1020 1021 sql.begin(); 1022 1023 { 1024 std::vector<int> in(3); 1025 in[0] = 3; 1026 in[1] = 2; 1027 in[2] = 1; 1028 1029 statement st = (sql.prepare << "insert into test11(id) values(?)", 1030 use(in)); 1031 st.execute(1); 1032 1033 // Note: Firebird backend inserts every row with separate insert 1034 // statement to achieve the effect of inserting vectors of values. 1035 // Since getRowCount() returns number of rows affected by the *last* 1036 // statement, it will return 1 here. 1037 CHECK(getRowCount(st, eRowsInserted) == 1); 1038 } 1039 1040 { 1041 int i = 5; 1042 statement st = (sql.prepare << "update test11 set id = ? where id<3", 1043 use(i)); 1044 st.execute(1); 1045 CHECK(getRowCount(st, eRowsUpdated) == 2); 1046 1047 // verify that no rows were deleted 1048 CHECK(getRowCount(st, eRowsDeleted) == 0); 1049 } 1050 1051 { 1052 std::vector<int> out(3); 1053 statement st = (sql.prepare << "select id from test11", into(out)); 1054 st.execute(1); 1055 1056 CHECK(getRowCount(st, eRowsSelected) == 3); 1057 } 1058 1059 { 1060 statement st = (sql.prepare << "delete from test11 where id=10"); 1061 st.execute(1); 1062 CHECK(getRowCount(st, eRowsDeleted) == 0); 1063 } 1064 1065 { 1066 statement st = (sql.prepare << "delete from test11"); 1067 st.execute(1); 1068 CHECK(getRowCount(st, eRowsDeleted) == 3); 1069 } 1070 1071 sql << "drop table test11"; 1072 } 1073 1074 TEST_CASE("Firebird string coercions", "[firebird][string]") 1075 { 1076 soci::session sql(backEnd, connectString); 1077 1078 try 1079 { 1080 sql << "drop table test12"; 1081 } 1082 catch (std::runtime_error &) 1083 {} // ignore if error 1084 1085 sql << "create table test12(a decimal(10,3), b timestamp, c date, d time)"; 1086 sql.commit(); 1087 sql.begin(); 1088 1089 // Check if passing input parameters as strings works 1090 // for different column types. 1091 { 1092 std::string a = "-3.14150", b = "2013-02-28 23:36:01", 1093 c = "2013-02-28", d = "23:36:01"; 1094 statement st = (sql.prepare << 1095 "insert into test12(a, b, c, d) values (?, ?, ?, ?)", 1096 use(a), use(b), use(c), use(d)); 1097 st.execute(1); 1098 CHECK(getRowCount(st, eRowsInserted) == 1); 1099 } 1100 1101 { 1102 double a; 1103 std::tm b = std::tm(), c = std::tm(), d = std::tm(); 1104 sql << "select a, b, c, d from test12", 1105 into(a), into(b), into(c), into(d); 1106 CHECK(std::fabs(a - (-3.141)) < 0.000001); 1107 CHECK(b.tm_year == 2013 - 1900); 1108 CHECK(b.tm_mon == 2 - 1); 1109 CHECK(b.tm_mday == 28); 1110 CHECK(b.tm_hour == 23); 1111 CHECK(b.tm_min == 36); 1112 CHECK(b.tm_sec == 1); 1113 CHECK(c.tm_year == 2013 - 1900); 1114 CHECK(c.tm_mon == 2 - 1); 1115 CHECK(c.tm_mday == 28); 1116 CHECK(c.tm_hour == 0); 1117 CHECK(c.tm_min == 0); 1118 CHECK(c.tm_sec == 0); 1119 CHECK(d.tm_hour == 23); 1120 CHECK(d.tm_min == 36); 1121 CHECK(d.tm_sec == 1); 1122 } 1123 1124 sql << "drop table test12"; 1125 } 1126 1127 // Dynamic binding to row objects: decimals_as_strings 1128 TEST_CASE("Firebird decimals as strings", "[firebird][decimal][string]") 1129 { 1130 using namespace soci::details::firebird; 1131 1132 int a = -12345678; 1133 CHECK(format_decimal<int>(&a, 1) == "-123456780"); 1134 CHECK(format_decimal<int>(&a, 0) == "-12345678"); 1135 CHECK(format_decimal<int>(&a, -3) == "-12345.678"); 1136 CHECK(format_decimal<int>(&a, -8) == "-0.12345678"); 1137 CHECK(format_decimal<int>(&a, -9) == "-0.012345678"); 1138 1139 a = 12345678; 1140 CHECK(format_decimal<int>(&a, 1) == "123456780"); 1141 CHECK(format_decimal<int>(&a, 0) == "12345678"); 1142 CHECK(format_decimal<int>(&a, -3) == "12345.678"); 1143 CHECK(format_decimal<int>(&a, -8) == "0.12345678"); 1144 CHECK(format_decimal<int>(&a, -9) == "0.012345678"); 1145 1146 soci::session sql(backEnd, connectString + " decimals_as_strings=1"); 1147 1148 try 1149 { 1150 sql << "drop table test13"; 1151 } 1152 catch (std::runtime_error &) 1153 {} // ignore if error 1154 1155 sql << "create table test13(ntest1 decimal(10,2), " 1156 << "ntest2 decimal(4,4), ntest3 decimal(3,1))"; 1157 sql.commit(); 1158 1159 sql.begin(); 1160 1161 { 1162 row r; 1163 sql << "select * from test13", into(r); 1164 CHECK(sql.got_data() == false); 1165 } 1166 1167 std::string d_str0("+03.140"), d_str1("3.14"), 1168 d_str2("3.1400"), d_str3("3.1"); 1169 indicator ind(i_ok); 1170 1171 { 1172 statement st((sql.prepare << 1173 "insert into test13(ntest1, ntest2, ntest3) " 1174 "values(:ntest1, :ntest2, :ntest3)", 1175 use(d_str0, ind, "ntest1"), use(d_str0, "ntest2"), 1176 use(d_str0, "ntest3"))); 1177 1178 st.execute(1); 1179 1180 ind = i_null; 1181 st.execute(1); 1182 } 1183 1184 row r; 1185 statement st = (sql.prepare << "select * from test13", into(r)); 1186 st.execute(1); 1187 1188 CHECK(r.size() == 3); 1189 1190 // get properties by position 1191 CHECK(r.get_properties(0).get_name() == "NTEST1"); 1192 CHECK(r.get_properties(0).get_data_type() == dt_string); 1193 CHECK(r.get_properties(1).get_name() == "NTEST2"); 1194 CHECK(r.get_properties(1).get_data_type() == dt_string); 1195 CHECK(r.get_properties(2).get_name() == "NTEST3"); 1196 CHECK(r.get_properties(2).get_data_type() == dt_string); 1197 1198 // get properties by name 1199 CHECK(r.get_properties("NTEST1").get_name() == "NTEST1"); 1200 CHECK(r.get_properties("NTEST1").get_data_type() == dt_string); 1201 CHECK(r.get_properties("NTEST2").get_name() == "NTEST2"); 1202 CHECK(r.get_properties("NTEST2").get_data_type() == dt_string); 1203 CHECK(r.get_properties("NTEST3").get_name() == "NTEST3"); 1204 CHECK(r.get_properties("NTEST3").get_data_type() == dt_string); 1205 1206 // get values by position 1207 CHECK(r.get<std::string>(0) == d_str1); 1208 CHECK(r.get<std::string>(1) == d_str2); 1209 CHECK(r.get<std::string>(2) == d_str3); 1210 1211 // get values by name 1212 CHECK(r.get<std::string>("NTEST1") == d_str1); 1213 CHECK(r.get<std::string>("NTEST2") == d_str2); 1214 CHECK(r.get<std::string>("NTEST3") == d_str3); 1215 1216 st.fetch(); 1217 CHECK(r.get_indicator(0) == i_null); 1218 CHECK(r.get_indicator(1) == i_ok); 1219 CHECK(r.get_indicator(2) == i_ok); 1220 1221 sql << "drop table test13"; 1222 } 1223 1224 // 1225 // Support for soci Common Tests 1226 // 1227 1228 struct TableCreator1 : public tests::table_creator_base 1229 { 1230 TableCreator1(soci::session & sql) 1231 : tests::table_creator_base(sql) 1232 { 1233 sql << "create table soci_test(id integer, val integer, c char, " 1234 "str varchar(20), sh smallint, ul bigint, d double precision, " 1235 "num76 numeric(7,6), " 1236 "tm timestamp, i1 integer, i2 integer, i3 integer, name varchar(20))"; 1237 sql.commit(); 1238 sql.begin(); 1239 } 1240 }; 1241 1242 struct TableCreator2 : public tests::table_creator_base 1243 { 1244 TableCreator2(soci::session & sql) 1245 : tests::table_creator_base(sql) 1246 { 1247 sql << "create table soci_test(num_float float, num_int integer, " 1248 "name varchar(20), sometime timestamp, chr char)"; 1249 sql.commit(); 1250 sql.begin(); 1251 } 1252 }; 1253 1254 struct TableCreator3 : public tests::table_creator_base 1255 { 1256 TableCreator3(soci::session & sql) 1257 : tests::table_creator_base(sql) 1258 { 1259 sql << "create table soci_test(name varchar(100) not null, " 1260 "phone varchar(15))"; 1261 sql.commit(); 1262 sql.begin(); 1263 } 1264 }; 1265 1266 struct TableCreator4 : public tests::table_creator_base 1267 { 1268 TableCreator4(soci::session & sql) 1269 : tests::table_creator_base(sql) 1270 { 1271 sql << "create table soci_test(val integer)"; 1272 sql.commit(); 1273 sql.begin(); 1274 } 1275 }; 1276 1277 struct TableCreatorCLOB : public tests::table_creator_base 1278 { 1279 TableCreatorCLOB(soci::session & sql) 1280 : tests::table_creator_base(sql) 1281 { 1282 sql << "create table soci_test(id integer, s blob sub_type text)"; 1283 sql.commit(); 1284 sql.begin(); 1285 } 1286 }; 1287 1288 struct TableCreatorXML : public tests::table_creator_base 1289 { 1290 TableCreatorXML(soci::session & sql) 1291 : tests::table_creator_base(sql) 1292 { 1293 sql << "create table soci_test(id integer, x blob sub_type text)"; 1294 sql.commit(); 1295 sql.begin(); 1296 } 1297 }; 1298 1299 class test_context : public tests::test_context_base 1300 { 1301 public: 1302 test_context(backend_factory const &backEnd, 1303 std::string const &connectString) 1304 : test_context_base(backEnd, connectString) 1305 {} 1306 1307 tests::table_creator_base* table_creator_1(soci::session& s) const override 1308 { 1309 return new TableCreator1(s); 1310 } 1311 1312 tests::table_creator_base* table_creator_2(soci::session& s) const override 1313 { 1314 return new TableCreator2(s); 1315 } 1316 1317 tests::table_creator_base* table_creator_3(soci::session& s) const override 1318 { 1319 return new TableCreator3(s); 1320 } 1321 1322 tests::table_creator_base* table_creator_4(soci::session& s) const override 1323 { 1324 return new TableCreator4(s); 1325 } 1326 1327 tests::table_creator_base* table_creator_clob(soci::session& s) const override 1328 { 1329 return new TableCreatorCLOB(s); 1330 } 1331 1332 tests::table_creator_base* table_creator_xml(soci::session& s) const override 1333 { 1334 return new TableCreatorXML(s); 1335 } 1336 1337 std::string to_date_time(std::string const &datdt_string) const override 1338 { 1339 return "'" + datdt_string + "'"; 1340 } 1341 1342 void on_after_ddl(soci::session& sql) const override 1343 { 1344 sql.commit(); 1345 } 1346 1347 std::string sql_length(std::string const& s) const override 1348 { 1349 return "char_length(" + s + ")"; 1350 } 1351 }; 1352 1353 1354 int main(int argc, char** argv) 1355 { 1356 1357 #ifdef _MSC_VER 1358 // Redirect errors, unrecoverable problems, and assert() failures to STDERR, 1359 // instead of debug message window. 1360 // This hack is required to run assert()-driven tests by Buildbot. 1361 // NOTE: Comment this 2 lines for debugging with Visual C++ debugger to catch assertions inside. 1362 _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE); 1363 _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR); 1364 #endif //_MSC_VER 1365 1366 if (argc >= 2) 1367 { 1368 connectString = argv[1]; 1369 1370 // Replace the connect string with the process name to ensure that 1371 // CATCH uses the correct name in its messages. 1372 argv[1] = argv[0]; 1373 1374 argc--; 1375 argv++; 1376 } 1377 else 1378 { 1379 std::cout << "usage: " << argv[0] 1380 << " connectstring [test-arguments...]\n" 1381 << "example: " << argv[0] 1382 << " \"service=/usr/local/firebird/db/test.fdb user=SYSDBA password=masterkey\"\n"; 1383 return EXIT_FAILURE; 1384 } 1385 1386 test_context tc(backEnd, connectString); 1387 1388 return Catch::Session().run(argc, argv); 1389 }