Warning, file /sdk/codevis/thirdparty/soci/tests/postgresql/test-postgresql.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-2008 Maciej Sobczak, Stephen Hutton 0003 // Distributed under the Boost Software License, Version 1.0. 0004 // (See accompanying file LICENSE_1_0.txt or copy at 0005 // http://www.boost.org/LICENSE_1_0.txt) 0006 // 0007 0008 #include "soci/soci.h" 0009 #include "soci/postgresql/soci-postgresql.h" 0010 #include "common-tests.h" 0011 #include <iostream> 0012 #include <sstream> 0013 #include <string> 0014 #include <cmath> 0015 #include <cstring> 0016 #include <ctime> 0017 #include <cstdlib> 0018 0019 using namespace soci; 0020 using namespace soci::tests; 0021 0022 std::string connectString; 0023 backend_factory const &backEnd = *soci::factory_postgresql(); 0024 0025 // Postgres-specific tests 0026 0027 enum TestStringEnum 0028 { 0029 VALUE_STR_1=0, 0030 VALUE_STR_2, 0031 VALUE_STR_3 0032 }; 0033 0034 enum TestIntEnum 0035 { 0036 VALUE_INT_1=0, 0037 VALUE_INT_2, 0038 VALUE_INT_3 0039 }; 0040 0041 namespace soci { 0042 template <> struct type_conversion<TestStringEnum> 0043 { 0044 typedef std::string base_type; 0045 static void from_base(const std::string & v, indicator & ind, TestStringEnum & p) 0046 { 0047 if ( ind == i_null ) 0048 throw soci_error("Null value not allowed for this type"); 0049 0050 if ( v.compare("A") == 0 ) 0051 p = TestStringEnum::VALUE_STR_1; 0052 else if ( v.compare("B") == 0 ) 0053 p = TestStringEnum::VALUE_STR_2; 0054 else if ( v.compare("C") == 0 ) 0055 p = TestStringEnum::VALUE_STR_3; 0056 else 0057 throw soci_error("Value not allowed for this type"); 0058 } 0059 static void to_base(TestStringEnum & p, std::string & v, indicator & ind) 0060 { 0061 switch ( p ) 0062 { 0063 case TestStringEnum::VALUE_STR_1: 0064 v = "A"; 0065 ind = i_ok; 0066 return; 0067 case TestStringEnum::VALUE_STR_2: 0068 v = "B"; 0069 ind = i_ok; 0070 return; 0071 case TestStringEnum::VALUE_STR_3: 0072 v = "C"; 0073 ind = i_ok; 0074 return; 0075 default: 0076 throw soci_error("Value not allowed for this type"); 0077 } 0078 } 0079 }; 0080 0081 template <> struct type_conversion<TestIntEnum> 0082 { 0083 typedef int base_type; 0084 static void from_base(const int & v, indicator & ind, TestIntEnum & p) 0085 { 0086 if ( ind == i_null ) 0087 throw soci_error("Null value not allowed for this type"); 0088 0089 switch( v ) 0090 { 0091 case 0: 0092 case 1: 0093 case 2: 0094 p = (TestIntEnum)v; 0095 ind = i_ok; 0096 return; 0097 default: 0098 throw soci_error("Null value not allowed for this type"); 0099 } 0100 } 0101 static void to_base(TestIntEnum & p, int & v, indicator & ind) 0102 { 0103 switch( p ) 0104 { 0105 case TestIntEnum::VALUE_INT_1: 0106 case TestIntEnum::VALUE_INT_2: 0107 case TestIntEnum::VALUE_INT_3: 0108 v = (int)p; 0109 ind = i_ok; 0110 return; 0111 default: 0112 throw soci_error("Value not allowed for this type"); 0113 } 0114 } 0115 }; 0116 } 0117 0118 struct oid_table_creator : public table_creator_base 0119 { 0120 oid_table_creator(soci::session& sql) 0121 : table_creator_base(sql) 0122 { 0123 sql << "create table soci_test (" 0124 " id integer," 0125 " name varchar(100)" 0126 ") with oids"; 0127 } 0128 }; 0129 0130 // ROWID test 0131 // Note: in PostgreSQL, there is no ROWID, there is OID. 0132 // It is still provided as a separate type for "portability", 0133 // whatever that means. 0134 TEST_CASE("PostgreSQL ROWID", "[postgresql][rowid][oid]") 0135 { 0136 soci::session sql(backEnd, connectString); 0137 0138 int server_version_num; 0139 sql << "show server_version_num", into(server_version_num); 0140 if ( server_version_num >= 120000 ) 0141 { 0142 WARN("Skipping test because OIDs are no longer supported in PostgreSQL " 0143 << server_version_num); 0144 return; 0145 } 0146 0147 oid_table_creator tableCreator(sql); 0148 0149 sql << "insert into soci_test(id, name) values(7, \'John\')"; 0150 0151 rowid rid(sql); 0152 sql << "select oid from soci_test where id = 7", into(rid); 0153 0154 int id; 0155 std::string name; 0156 0157 sql << "select id, name from soci_test where oid = :rid", 0158 into(id), into(name), use(rid); 0159 0160 CHECK(id == 7); 0161 CHECK(name == "John"); 0162 } 0163 0164 TEST_CASE("PostgreSQL prepare error", "[postgresql][exception]") 0165 { 0166 soci::session sql(backEnd, connectString); 0167 0168 // Must not cause the application to crash. 0169 statement st(sql); 0170 st.prepare(""); // Throws an exception in some versions. 0171 } 0172 0173 // function call test 0174 class function_creator : function_creator_base 0175 { 0176 public: 0177 0178 function_creator(soci::session & sql) 0179 : function_creator_base(sql) 0180 { 0181 // before a language can be used it must be defined 0182 // if it has already been defined then an error will occur 0183 try { sql << "create language plpgsql"; } 0184 catch (soci_error const &) {} // ignore if error 0185 0186 sql << 0187 "create or replace function soci_test(msg varchar) " 0188 "returns varchar as $$ " 0189 "declare x int := 1;" 0190 "begin " 0191 " return msg; " 0192 "end $$ language plpgsql"; 0193 } 0194 0195 protected: 0196 0197 std::string drop_statement() 0198 { 0199 return "drop function soci_test(varchar)"; 0200 } 0201 }; 0202 0203 TEST_CASE("PostgreSQL function call", "[postgresql][function]") 0204 { 0205 soci::session sql(backEnd, connectString); 0206 0207 function_creator functionCreator(sql); 0208 0209 std::string in("my message"); 0210 std::string out; 0211 0212 statement st = (sql.prepare << 0213 "select soci_test(:input)", 0214 into(out), 0215 use(in, "input")); 0216 0217 st.execute(true); 0218 CHECK(out == in); 0219 0220 // explicit procedure syntax 0221 { 0222 procedure proc = (sql.prepare << 0223 "soci_test(:input)", 0224 into(out), use(in, "input")); 0225 0226 proc.execute(true); 0227 CHECK(out == in); 0228 } 0229 } 0230 0231 // BLOB test 0232 struct blob_table_creator : public table_creator_base 0233 { 0234 blob_table_creator(soci::session & sql) 0235 : table_creator_base(sql) 0236 { 0237 sql << 0238 "create table soci_test (" 0239 " id integer," 0240 " img oid" 0241 ")"; 0242 } 0243 }; 0244 0245 TEST_CASE("PostgreSQL blob", "[postgresql][blob]") 0246 { 0247 soci::session sql(backEnd, connectString); 0248 0249 blob_table_creator tableCreator(sql); 0250 0251 char buf[] = "abcdefghijklmnopqrstuvwxyz"; 0252 0253 sql << "insert into soci_test(id, img) values(7, lo_creat(-1))"; 0254 0255 // in PostgreSQL, BLOB operations must be within transaction block 0256 transaction tr(sql); 0257 0258 { 0259 blob b(sql); 0260 0261 sql << "select img from soci_test where id = 7", into(b); 0262 CHECK(b.get_len() == 0); 0263 0264 b.write_from_start(buf, sizeof(buf)); 0265 CHECK(b.get_len() == sizeof(buf)); 0266 0267 b.append(buf, sizeof(buf)); 0268 CHECK(b.get_len() == 2 * sizeof(buf)); 0269 } 0270 { 0271 blob b(sql); 0272 sql << "select img from soci_test where id = 7", into(b); 0273 CHECK(b.get_len() == 2 * sizeof(buf)); 0274 char buf2[100]; 0275 b.read_from_start(buf2, 10); 0276 CHECK(std::strncmp(buf2, "abcdefghij", 10) == 0); 0277 } 0278 0279 unsigned long oid; 0280 sql << "select img from soci_test where id = 7", into(oid); 0281 sql << "select lo_unlink(" << oid << ")"; 0282 } 0283 0284 struct longlong_table_creator : table_creator_base 0285 { 0286 longlong_table_creator(soci::session & sql) 0287 : table_creator_base(sql) 0288 { 0289 sql << "create table soci_test(val int8)"; 0290 } 0291 }; 0292 0293 // long long test 0294 TEST_CASE("PostgreSQL long long", "[postgresql][longlong]") 0295 { 0296 soci::session sql(backEnd, connectString); 0297 0298 longlong_table_creator tableCreator(sql); 0299 0300 long long v1 = 1000000000000LL; 0301 sql << "insert into soci_test(val) values(:val)", use(v1); 0302 0303 long long v2 = 0LL; 0304 sql << "select val from soci_test", into(v2); 0305 0306 CHECK(v2 == v1); 0307 } 0308 0309 // vector<long long> 0310 TEST_CASE("PostgreSQL vector long long", "[postgresql][vector][longlong]") 0311 { 0312 soci::session sql(backEnd, connectString); 0313 0314 longlong_table_creator tableCreator(sql); 0315 0316 std::vector<long long> v1; 0317 v1.push_back(1000000000000LL); 0318 v1.push_back(1000000000001LL); 0319 v1.push_back(1000000000002LL); 0320 v1.push_back(1000000000003LL); 0321 v1.push_back(1000000000004LL); 0322 0323 sql << "insert into soci_test(val) values(:val)", use(v1); 0324 0325 std::vector<long long> v2(10); 0326 sql << "select val from soci_test order by val desc", into(v2); 0327 0328 REQUIRE(v2.size() == 5); 0329 CHECK(v2[0] == 1000000000004LL); 0330 CHECK(v2[1] == 1000000000003LL); 0331 CHECK(v2[2] == 1000000000002LL); 0332 CHECK(v2[3] == 1000000000001LL); 0333 CHECK(v2[4] == 1000000000000LL); 0334 } 0335 0336 // unsigned long long test 0337 TEST_CASE("PostgreSQL unsigned long long", "[postgresql][unsigned][longlong]") 0338 { 0339 soci::session sql(backEnd, connectString); 0340 0341 longlong_table_creator tableCreator(sql); 0342 0343 unsigned long long v1 = 1000000000000ULL; 0344 sql << "insert into soci_test(val) values(:val)", use(v1); 0345 0346 unsigned long long v2 = 0ULL; 0347 sql << "select val from soci_test", into(v2); 0348 0349 CHECK(v2 == v1); 0350 } 0351 0352 struct boolean_table_creator : table_creator_base 0353 { 0354 boolean_table_creator(soci::session & sql) 0355 : table_creator_base(sql) 0356 { 0357 sql << "create table soci_test(val boolean)"; 0358 } 0359 }; 0360 0361 TEST_CASE("PostgreSQL boolean", "[postgresql][boolean]") 0362 { 0363 soci::session sql(backEnd, connectString); 0364 0365 boolean_table_creator tableCreator(sql); 0366 0367 int i1 = 0; 0368 0369 sql << "insert into soci_test(val) values(:val)", use(i1); 0370 0371 int i2 = 7; 0372 sql << "select val from soci_test", into(i2); 0373 0374 CHECK(i2 == i1); 0375 0376 sql << "update soci_test set val = true"; 0377 sql << "select val from soci_test", into(i2); 0378 CHECK(i2 == 1); 0379 } 0380 0381 struct uuid_table_creator : table_creator_base 0382 { 0383 uuid_table_creator(soci::session & sql) 0384 : table_creator_base(sql) 0385 { 0386 sql << "create table soci_test(val uuid)"; 0387 } 0388 }; 0389 0390 // uuid test 0391 TEST_CASE("PostgreSQL uuid", "[postgresql][uuid]") 0392 { 0393 soci::session sql(backEnd, connectString); 0394 0395 uuid_table_creator tableCreator(sql); 0396 0397 std::string v1("cd2dcb78-3817-442e-b12a-17c7e42669a0"); 0398 sql << "insert into soci_test(val) values(:val)", use(v1); 0399 0400 std::string v2; 0401 sql << "select val from soci_test", into(v2); 0402 0403 CHECK(v2 == v1); 0404 } 0405 0406 // dynamic backend test -- currently skipped by default 0407 TEST_CASE("PostgreSQL dynamic backend", "[postgresql][backend][.]") 0408 { 0409 try 0410 { 0411 soci::session sql("nosuchbackend://" + connectString); 0412 FAIL("expected exception not thrown"); 0413 } 0414 catch (soci_error const & e) 0415 { 0416 CHECK(e.get_error_message() == 0417 "Failed to open: libsoci_nosuchbackend.so"); 0418 } 0419 0420 { 0421 dynamic_backends::register_backend("pgsql", backEnd); 0422 0423 std::vector<std::string> backends = dynamic_backends::list_all(); 0424 REQUIRE(backends.size() == 1); 0425 CHECK(backends[0] == "pgsql"); 0426 0427 { 0428 soci::session sql("pgsql://" + connectString); 0429 } 0430 0431 dynamic_backends::unload("pgsql"); 0432 0433 backends = dynamic_backends::list_all(); 0434 CHECK(backends.empty()); 0435 } 0436 0437 { 0438 soci::session sql("postgresql://" + connectString); 0439 } 0440 } 0441 0442 TEST_CASE("PostgreSQL literals", "[postgresql][into]") 0443 { 0444 soci::session sql(backEnd, connectString); 0445 0446 int i; 0447 sql << "select 123", into(i); 0448 CHECK(i == 123); 0449 0450 try 0451 { 0452 sql << "select 'ABC'", into (i); 0453 FAIL("expected exception not thrown"); 0454 } 0455 catch (soci_error const & e) 0456 { 0457 char const * expectedPrefix = "Cannot convert data"; 0458 CAPTURE(e.what()); 0459 CHECK(strncmp(e.what(), expectedPrefix, strlen(expectedPrefix)) == 0); 0460 } 0461 } 0462 0463 TEST_CASE("PostgreSQL backend name", "[postgresql][backend]") 0464 { 0465 soci::session sql(backEnd, connectString); 0466 0467 CHECK(sql.get_backend_name() == "postgresql"); 0468 } 0469 0470 // test for double-colon cast in SQL expressions 0471 TEST_CASE("PostgreSQL double colon cast", "[postgresql][cast]") 0472 { 0473 soci::session sql(backEnd, connectString); 0474 0475 int a = 123; 0476 int b = 0; 0477 sql << "select :a::integer", use(a), into(b); 0478 CHECK(b == a); 0479 } 0480 0481 // test for date, time and timestamp parsing 0482 TEST_CASE("PostgreSQL datetime", "[postgresql][datetime]") 0483 { 0484 soci::session sql(backEnd, connectString); 0485 0486 std::string someDate = "2009-06-17 22:51:03.123"; 0487 std::tm t1 = std::tm(), t2 = std::tm(), t3 = std::tm(); 0488 0489 sql << "select :sd::date, :sd::time, :sd::timestamp", 0490 use(someDate, "sd"), into(t1), into(t2), into(t3); 0491 0492 // t1 should contain only the date part 0493 CHECK(t1.tm_year == 2009 - 1900); 0494 CHECK(t1.tm_mon == 6 - 1); 0495 CHECK(t1.tm_mday == 17); 0496 CHECK(t1.tm_hour == 0); 0497 CHECK(t1.tm_min == 0); 0498 CHECK(t1.tm_sec == 0); 0499 0500 // t2 should contain only the time of day part 0501 CHECK(t2.tm_year == 0); 0502 CHECK(t2.tm_mon == 0); 0503 CHECK(t2.tm_mday == 1); 0504 CHECK(t2.tm_hour == 22); 0505 CHECK(t2.tm_min == 51); 0506 CHECK(t2.tm_sec == 3); 0507 0508 // t3 should contain all information 0509 CHECK(t3.tm_year == 2009 - 1900); 0510 CHECK(t3.tm_mon == 6 - 1); 0511 CHECK(t3.tm_mday == 17); 0512 CHECK(t3.tm_hour == 22); 0513 CHECK(t3.tm_min == 51); 0514 CHECK(t3.tm_sec == 3); 0515 } 0516 0517 // test for number of affected rows 0518 0519 struct table_creator_for_test11 : table_creator_base 0520 { 0521 table_creator_for_test11(soci::session & sql) 0522 : table_creator_base(sql) 0523 { 0524 sql << "create table soci_test(val integer)"; 0525 } 0526 }; 0527 0528 TEST_CASE("PostgreSQL get affected rows", "[postgresql][affected-rows]") 0529 { 0530 soci::session sql(backEnd, connectString); 0531 0532 table_creator_for_test11 tableCreator(sql); 0533 0534 for (int i = 0; i != 10; i++) 0535 { 0536 sql << "insert into soci_test(val) values(:val)", use(i); 0537 } 0538 0539 statement st1 = (sql.prepare << 0540 "update soci_test set val = val + 1"); 0541 st1.execute(false); 0542 0543 CHECK(st1.get_affected_rows() == 10); 0544 0545 statement st2 = (sql.prepare << 0546 "delete from soci_test where val <= 5"); 0547 st2.execute(false); 0548 0549 CHECK(st2.get_affected_rows() == 5); 0550 } 0551 0552 // test INSERT INTO ... RETURNING syntax 0553 0554 struct table_creator_for_test12 : table_creator_base 0555 { 0556 table_creator_for_test12(soci::session & sql) 0557 : table_creator_base(sql) 0558 { 0559 sql << "create table soci_test(sid serial, txt text)"; 0560 } 0561 }; 0562 0563 TEST_CASE("PostgreSQL insert into ... returning", "[postgresql]") 0564 { 0565 soci::session sql(backEnd, connectString); 0566 0567 table_creator_for_test12 tableCreator(sql); 0568 0569 std::vector<long> ids(10); 0570 for (std::size_t i = 0; i != ids.size(); i++) 0571 { 0572 long sid(0); 0573 std::string txt("abc"); 0574 sql << "insert into soci_test(txt) values(:txt) returning sid", use(txt, "txt"), into(sid); 0575 ids[i] = sid; 0576 } 0577 0578 std::vector<long> ids2(ids.size()); 0579 sql << "select sid from soci_test order by sid", into(ids2); 0580 CHECK(std::equal(ids.begin(), ids.end(), ids2.begin())); 0581 } 0582 0583 struct bytea_table_creator : public table_creator_base 0584 { 0585 bytea_table_creator(soci::session& sql) 0586 : table_creator_base(sql) 0587 { 0588 sql << "drop table if exists soci_test;"; 0589 sql << "create table soci_test ( val bytea null )"; 0590 } 0591 }; 0592 0593 TEST_CASE("PostgreSQL bytea", "[postgresql][bytea]") 0594 { 0595 soci::session sql(backEnd, connectString); 0596 0597 // PostgreSQL supports two different output formats for bytea values: 0598 // historical "escape" format, which is the only one supported until 0599 // PostgreSQL 9.0, and "hex" format used by default since 9.0, we need 0600 // to determine which one is actually in use. 0601 std::string bytea_output_format; 0602 sql << "select setting from pg_settings where name='bytea_output'", 0603 into(bytea_output_format); 0604 char const* expectedBytea; 0605 if (bytea_output_format.empty() || bytea_output_format == "escape") 0606 expectedBytea = "\\015\\014\\013\\012"; 0607 else if (bytea_output_format == "hex") 0608 expectedBytea = "\\x0d0c0b0a"; 0609 else 0610 throw std::runtime_error("Unknown PostgreSQL bytea_output \"" + 0611 bytea_output_format + "\""); 0612 0613 bytea_table_creator tableCreator(sql); 0614 0615 int v = 0x0A0B0C0D; 0616 unsigned char* b = reinterpret_cast<unsigned char*>(&v); 0617 std::string data; 0618 std::copy(b, b + sizeof(v), std::back_inserter(data)); 0619 { 0620 0621 sql << "insert into soci_test(val) values(:val)", use(data); 0622 0623 // 1) into string, no Oid mapping 0624 std::string bin1; 0625 sql << "select val from soci_test", into(bin1); 0626 CHECK(bin1 == expectedBytea); 0627 0628 // 2) Oid-to-dt_string mapped 0629 row r; 0630 sql << "select * from soci_test", into(r); 0631 0632 REQUIRE(r.size() == 1); 0633 column_properties const& props = r.get_properties(0); 0634 CHECK(props.get_data_type() == soci::dt_string); 0635 std::string bin2 = r.get<std::string>(0); 0636 CHECK(bin2 == expectedBytea); 0637 } 0638 } 0639 0640 // json 0641 struct table_creator_json : public table_creator_base 0642 { 0643 table_creator_json(soci::session& sql) 0644 : table_creator_base(sql) 0645 { 0646 sql << "drop table if exists soci_json_test;"; 0647 sql << "create table soci_json_test(data json)"; 0648 } 0649 }; 0650 0651 // Return 9,2 for 9.2.3 0652 typedef std::pair<int,int> server_version; 0653 0654 server_version get_postgresql_version(soci::session& sql) 0655 { 0656 std::string version; 0657 std::pair<int,int> result; 0658 sql << "select version()",into(version); 0659 if (sscanf(version.c_str(),"PostgreSQL %i.%i", &result.first, &result.second) < 2) 0660 { 0661 throw std::runtime_error("Failed to retrieve PostgreSQL version number"); 0662 } 0663 return result; 0664 } 0665 0666 // Test JSON. Only valid for PostgreSQL Server 9.2++ 0667 TEST_CASE("PostgreSQL JSON", "[postgresql][json]") 0668 { 0669 soci::session sql(backEnd, connectString); 0670 server_version version = get_postgresql_version(sql); 0671 if ( version >= server_version(9,2)) 0672 { 0673 std::string result; 0674 std::string valid_input = "{\"tool\":\"soci\",\"result\":42}"; 0675 std::string invalid_input = "{\"tool\":\"other\",\"result\":invalid}"; 0676 0677 table_creator_json tableCreator(sql); 0678 0679 sql << "insert into soci_json_test (data) values(:data)",use(valid_input); 0680 sql << "select data from soci_json_test",into(result); 0681 CHECK(result == valid_input); 0682 0683 CHECK_THROWS_AS(( 0684 sql << "insert into soci_json_test (data) values(:data)",use(invalid_input)), 0685 soci_error 0686 ); 0687 } 0688 else 0689 { 0690 WARN("JSON test skipped (PostgreSQL >= 9.2 required, found " << version.first << "." << version.second << ")"); 0691 } 0692 } 0693 0694 struct table_creator_text : public table_creator_base 0695 { 0696 table_creator_text(soci::session& sql) : table_creator_base(sql) 0697 { 0698 sql << "drop table if exists soci_test;"; 0699 sql << "create table soci_test(name varchar(20))"; 0700 } 0701 }; 0702 0703 // Test deallocate_prepared_statement called for non-existing statement 0704 // which creation failed due to invalid SQL syntax. 0705 // https://github.com/SOCI/soci/issues/116 0706 TEST_CASE("PostgreSQL statement prepare failure", "[postgresql][prepare]") 0707 { 0708 soci::session sql(backEnd, connectString); 0709 table_creator_text tableCreator(sql); 0710 0711 try 0712 { 0713 // types mismatch should lead to PQprepare failure 0714 statement get_trades = 0715 (sql.prepare 0716 << "select * from soci_test where name=9999"); 0717 FAIL("expected exception not thrown"); 0718 } 0719 catch(soci_error const& e) 0720 { 0721 std::string const msg(e.what()); 0722 CAPTURE(msg); 0723 0724 // poor-man heuristics 0725 CHECK(msg.find("prepared statement") == std::string::npos); 0726 CHECK(msg.find("operator does not exist") != std::string::npos); 0727 } 0728 } 0729 0730 // Test the support of PostgreSQL-style casts with ORM 0731 TEST_CASE("PostgreSQL ORM cast", "[postgresql][orm]") 0732 { 0733 soci::session sql(backEnd, connectString); 0734 values v; 0735 v.set("a", 1); 0736 sql << "select :a::int", use(v); // Must not throw an exception! 0737 } 0738 0739 // Test the DDL and metadata functionality 0740 TEST_CASE("PostgreSQL DDL with metadata", "[postgresql][ddl]") 0741 { 0742 soci::session sql(backEnd, connectString); 0743 0744 // note: prepare_column_descriptions expects l-value 0745 std::string ddl_t1 = "ddl_t1"; 0746 std::string ddl_t2 = "ddl_t2"; 0747 std::string ddl_t3 = "ddl_t3"; 0748 0749 // single-expression variant: 0750 sql.create_table(ddl_t1).column("i", soci::dt_integer).column("j", soci::dt_integer); 0751 0752 // check whether this table was created: 0753 0754 bool ddl_t1_found = false; 0755 bool ddl_t2_found = false; 0756 bool ddl_t3_found = false; 0757 std::string table_name; 0758 soci::statement st = (sql.prepare_table_names(), into(table_name)); 0759 st.execute(); 0760 while (st.fetch()) 0761 { 0762 if (table_name == ddl_t1) { ddl_t1_found = true; } 0763 if (table_name == ddl_t2) { ddl_t2_found = true; } 0764 if (table_name == ddl_t3) { ddl_t3_found = true; } 0765 } 0766 0767 CHECK(ddl_t1_found); 0768 CHECK(ddl_t2_found == false); 0769 CHECK(ddl_t3_found == false); 0770 0771 // check whether ddl_t1 has the right structure: 0772 0773 bool i_found = false; 0774 bool j_found = false; 0775 bool other_found = false; 0776 soci::column_info ci; 0777 soci::statement st1 = (sql.prepare_column_descriptions(ddl_t1), into(ci)); 0778 st1.execute(); 0779 while (st1.fetch()) 0780 { 0781 if (ci.name == "i") 0782 { 0783 CHECK(ci.type == soci::dt_integer); 0784 CHECK(ci.nullable); 0785 i_found = true; 0786 } 0787 else if (ci.name == "j") 0788 { 0789 CHECK(ci.type == soci::dt_integer); 0790 CHECK(ci.nullable); 0791 j_found = true; 0792 } 0793 else 0794 { 0795 other_found = true; 0796 } 0797 } 0798 0799 CHECK(i_found); 0800 CHECK(j_found); 0801 CHECK(other_found == false); 0802 0803 // two more tables: 0804 0805 // separately defined columns: 0806 // (note: statement is executed when ddl object goes out of scope) 0807 { 0808 soci::ddl_type ddl = sql.create_table(ddl_t2); 0809 ddl.column("i", soci::dt_integer); 0810 ddl.column("j", soci::dt_integer); 0811 ddl.column("k", soci::dt_integer)("not null"); 0812 ddl.primary_key("t2_pk", "j"); 0813 } 0814 0815 sql.add_column(ddl_t1, "k", soci::dt_integer); 0816 sql.add_column(ddl_t1, "big", soci::dt_string, 0); // "unlimited" length -> text 0817 sql.drop_column(ddl_t1, "i"); 0818 0819 // or with constraint as in t2: 0820 sql.add_column(ddl_t2, "m", soci::dt_integer)("not null"); 0821 0822 // third table with a foreign key to the second one 0823 { 0824 soci::ddl_type ddl = sql.create_table(ddl_t3); 0825 ddl.column("x", soci::dt_integer); 0826 ddl.column("y", soci::dt_integer); 0827 ddl.foreign_key("t3_fk", "x", ddl_t2, "j"); 0828 } 0829 0830 // check if all tables were created: 0831 0832 ddl_t1_found = false; 0833 ddl_t2_found = false; 0834 ddl_t3_found = false; 0835 soci::statement st2 = (sql.prepare_table_names(), into(table_name)); 0836 st2.execute(); 0837 while (st2.fetch()) 0838 { 0839 if (table_name == ddl_t1) { ddl_t1_found = true; } 0840 if (table_name == ddl_t2) { ddl_t2_found = true; } 0841 if (table_name == ddl_t3) { ddl_t3_found = true; } 0842 } 0843 0844 CHECK(ddl_t1_found); 0845 CHECK(ddl_t2_found); 0846 CHECK(ddl_t3_found); 0847 0848 // check if ddl_t1 has the right structure (it was altered): 0849 0850 i_found = false; 0851 j_found = false; 0852 bool k_found = false; 0853 bool big_found = false; 0854 other_found = false; 0855 soci::statement st3 = (sql.prepare_column_descriptions(ddl_t1), into(ci)); 0856 st3.execute(); 0857 while (st3.fetch()) 0858 { 0859 if (ci.name == "j") 0860 { 0861 CHECK(ci.type == soci::dt_integer); 0862 CHECK(ci.nullable); 0863 j_found = true; 0864 } 0865 else if (ci.name == "k") 0866 { 0867 CHECK(ci.type == soci::dt_integer); 0868 CHECK(ci.nullable); 0869 k_found = true; 0870 } 0871 else if (ci.name == "big") 0872 { 0873 CHECK(ci.type == soci::dt_string); 0874 CHECK(ci.precision == 0); // "unlimited" for strings 0875 big_found = true; 0876 } 0877 else 0878 { 0879 other_found = true; 0880 } 0881 } 0882 0883 CHECK(i_found == false); 0884 CHECK(j_found); 0885 CHECK(k_found); 0886 CHECK(big_found); 0887 CHECK(other_found == false); 0888 0889 // check if ddl_t2 has the right structure: 0890 0891 i_found = false; 0892 j_found = false; 0893 k_found = false; 0894 bool m_found = false; 0895 other_found = false; 0896 soci::statement st4 = (sql.prepare_column_descriptions(ddl_t2), into(ci)); 0897 st4.execute(); 0898 while (st4.fetch()) 0899 { 0900 if (ci.name == "i") 0901 { 0902 CHECK(ci.type == soci::dt_integer); 0903 CHECK(ci.nullable); 0904 i_found = true; 0905 } 0906 else if (ci.name == "j") 0907 { 0908 CHECK(ci.type == soci::dt_integer); 0909 CHECK(ci.nullable == false); // primary key 0910 j_found = true; 0911 } 0912 else if (ci.name == "k") 0913 { 0914 CHECK(ci.type == soci::dt_integer); 0915 CHECK(ci.nullable == false); 0916 k_found = true; 0917 } 0918 else if (ci.name == "m") 0919 { 0920 CHECK(ci.type == soci::dt_integer); 0921 CHECK(ci.nullable == false); 0922 m_found = true; 0923 } 0924 else 0925 { 0926 other_found = true; 0927 } 0928 } 0929 0930 CHECK(i_found); 0931 CHECK(j_found); 0932 CHECK(k_found); 0933 CHECK(m_found); 0934 CHECK(other_found == false); 0935 0936 sql.drop_table(ddl_t1); 0937 sql.drop_table(ddl_t3); // note: this must be dropped before ddl_t2 0938 sql.drop_table(ddl_t2); 0939 0940 // check if all tables were dropped: 0941 0942 ddl_t1_found = false; 0943 ddl_t2_found = false; 0944 ddl_t3_found = false; 0945 st2 = (sql.prepare_table_names(), into(table_name)); 0946 st2.execute(); 0947 while (st2.fetch()) 0948 { 0949 if (table_name == ddl_t1) { ddl_t1_found = true; } 0950 if (table_name == ddl_t2) { ddl_t2_found = true; } 0951 if (table_name == ddl_t3) { ddl_t3_found = true; } 0952 } 0953 0954 CHECK(ddl_t1_found == false); 0955 CHECK(ddl_t2_found == false); 0956 CHECK(ddl_t3_found == false); 0957 0958 int i = -1; 0959 sql << "select lo_unlink(" + sql.empty_blob() + ")", into(i); 0960 CHECK(i == 1); 0961 sql << "select " + sql.nvl() + "(1, 2)", into(i); 0962 CHECK(i == 1); 0963 sql << "select " + sql.nvl() + "(NULL, 2)", into(i); 0964 CHECK(i == 2); 0965 } 0966 0967 // Test the bulk iterators functionality 0968 TEST_CASE("Bulk iterators", "[postgresql][bulkiters]") 0969 { 0970 soci::session sql(backEnd, connectString); 0971 0972 sql << "create table t (i integer)"; 0973 0974 // test bulk iterators with basic types 0975 { 0976 std::vector<int> v; 0977 v.push_back(10); 0978 v.push_back(20); 0979 v.push_back(30); 0980 v.push_back(40); 0981 v.push_back(50); 0982 0983 std::size_t begin = 2; 0984 std::size_t end = 5; 0985 sql << "insert into t (i) values (:v)", soci::use(v, begin, end); 0986 0987 v.clear(); 0988 v.resize(20); 0989 begin = 5; 0990 end = 20; 0991 sql << "select i from t", soci::into(v, begin, end); 0992 0993 CHECK(end == 8); 0994 for (std::size_t i = 0; i != 5; ++i) 0995 { 0996 CHECK(v[i] == 0); 0997 } 0998 CHECK(v[5] == 30); 0999 CHECK(v[6] == 40); 1000 CHECK(v[7] == 50); 1001 for (std::size_t i = end; i != 20; ++i) 1002 { 1003 CHECK(v[i] == 0); 1004 } 1005 } 1006 1007 sql << "delete from t"; 1008 1009 // test bulk iterators with user types 1010 { 1011 std::vector<MyInt> v; 1012 v.push_back(MyInt(10)); 1013 v.push_back(MyInt(20)); 1014 v.push_back(MyInt(30)); 1015 v.push_back(MyInt(40)); 1016 v.push_back(MyInt(50)); 1017 1018 std::size_t begin = 2; 1019 std::size_t end = 5; 1020 sql << "insert into t (i) values (:v)", soci::use(v, begin, end); 1021 1022 v.clear(); 1023 for (std::size_t i = 0; i != 20; ++i) 1024 { 1025 v.push_back(MyInt(-1)); 1026 } 1027 1028 begin = 5; 1029 end = 20; 1030 sql << "select i from t", soci::into(v, begin, end); 1031 1032 CHECK(end == 8); 1033 for (std::size_t i = 0; i != 5; ++i) 1034 { 1035 CHECK(v[i].get() == -1); 1036 } 1037 CHECK(v[5].get() == 30); 1038 CHECK(v[6].get() == 40); 1039 CHECK(v[7].get() == 50); 1040 for (std::size_t i = end; i != 20; ++i) 1041 { 1042 CHECK(v[i].get() == -1); 1043 } 1044 } 1045 1046 sql << "drop table t"; 1047 } 1048 1049 1050 // false_bind_variable_inside_identifier 1051 struct test_false_bind_variable_inside_identifier_table_creator : table_creator_base 1052 { 1053 test_false_bind_variable_inside_identifier_table_creator(soci::session & sql) 1054 : table_creator_base(sql) 1055 , msession(sql) 1056 { 1057 1058 try 1059 { 1060 sql << "CREATE TABLE soci_test( \"column_with:colon\" integer)"; 1061 sql << "CREATE TYPE \"type_with:colon\" AS ENUM ('en_one', 'en_two');"; 1062 sql << "CREATE FUNCTION \"function_with:colon\"() RETURNS integer LANGUAGE 'sql' AS " 1063 "$BODY$" 1064 " SELECT \"column_with:colon\" FROM soci_test LIMIT 1; " 1065 "$BODY$;" 1066 ; 1067 } 1068 catch(...) 1069 { 1070 drop(); 1071 } 1072 1073 } 1074 ~test_false_bind_variable_inside_identifier_table_creator(){ 1075 drop(); 1076 } 1077 private: 1078 void drop() 1079 { 1080 try 1081 { 1082 msession << "DROP FUNCTION IF EXISTS \"function_with:colon\"();"; 1083 msession << "DROP TYPE IF EXISTS \"type_with:colon\" ;"; 1084 } 1085 catch (soci_error const&){} 1086 } 1087 soci::session& msession; 1088 }; 1089 TEST_CASE("false_bind_variable_inside_identifier", "[postgresql][bind-variables]") 1090 { 1091 std::string col_name; 1092 int fct_return_value; 1093 std::string type_value; 1094 1095 { 1096 soci::session sql(backEnd, connectString); 1097 test_false_bind_variable_inside_identifier_table_creator tableCreator(sql); 1098 1099 sql << "insert into soci_test(\"column_with:colon\") values(2020)"; 1100 sql << "SELECT column_name FROM information_schema.columns WHERE table_schema = current_schema() AND table_name = 'soci_test';", into(col_name); 1101 sql << "SELECT \"function_with:colon\"() ;", into(fct_return_value); 1102 sql << "SELECT unnest(enum_range(NULL::\"type_with:colon\")) ORDER BY 1 LIMIT 1;", into(type_value); 1103 } 1104 1105 CHECK(col_name.compare("column_with:colon") == 0); 1106 CHECK(fct_return_value == 2020); 1107 CHECK(type_value.compare("en_one")==0); 1108 } 1109 1110 // test_enum_with_explicit_custom_type_string_rowset 1111 struct test_enum_with_explicit_custom_type_string_rowset : table_creator_base 1112 { 1113 test_enum_with_explicit_custom_type_string_rowset(soci::session & sql) 1114 : table_creator_base(sql) 1115 , msession(sql) 1116 { 1117 try 1118 { 1119 sql << "CREATE TYPE EnumType AS ENUM ('A','B','C');"; 1120 sql << "CREATE TABLE soci_test (Type EnumType NOT NULL DEFAULT 'A');"; 1121 } 1122 catch (...) 1123 { 1124 drop(); 1125 } 1126 1127 } 1128 ~test_enum_with_explicit_custom_type_string_rowset() 1129 { 1130 drop(); 1131 } 1132 1133 private: 1134 void drop() 1135 { 1136 try 1137 { 1138 msession << "drop table if exists soci_test;"; 1139 msession << "DROP TYPE IF EXISTS EnumType ;"; 1140 } 1141 catch (soci_error const& e){ 1142 std::cerr << e.what() << std::endl; 1143 } 1144 } 1145 soci::session& msession; 1146 }; 1147 1148 TEST_CASE("test_enum_with_explicit_custom_type_string_rowset", "[postgresql][bind-variables]") 1149 { 1150 TestStringEnum test_value = TestStringEnum::VALUE_STR_2; 1151 TestStringEnum type_value; 1152 1153 { 1154 soci::session sql(backEnd, connectString); 1155 test_enum_with_explicit_custom_type_string_rowset tableCreator(sql); 1156 1157 statement s1 = (sql.prepare << "insert into soci_test values(:val);", use(test_value, "val")); 1158 statement s2 = (sql.prepare << "SELECT Type FROM soci_test;"); 1159 1160 s1.execute(false); 1161 1162 soci::row result; 1163 s2.define_and_bind(); 1164 s2.exchange_for_rowset(soci::into(result)); 1165 s2.execute(true); 1166 1167 type_value = result.get<TestStringEnum>("type"); 1168 } 1169 1170 CHECK(type_value==TestStringEnum::VALUE_STR_2); 1171 } 1172 1173 TEST_CASE("test_enum_with_explicit_custom_type_string_into", "[postgresql][bind-variables]") 1174 { 1175 TestStringEnum test_value = TestStringEnum::VALUE_STR_2; 1176 TestStringEnum type_value; 1177 1178 { 1179 soci::session sql(backEnd, connectString); 1180 test_enum_with_explicit_custom_type_string_rowset tableCreator(sql); 1181 1182 statement s1 = (sql.prepare << "insert into soci_test values(:val);", use(test_value, "val")); 1183 statement s2 = (sql.prepare << "SELECT Type FROM soci_test;", into(type_value)); 1184 1185 s1.execute(false); 1186 s2.execute(true); 1187 } 1188 1189 CHECK(type_value==TestStringEnum::VALUE_STR_2); 1190 } 1191 1192 // test_enum_with_explicit_custom_type_int_rowset 1193 struct test_enum_with_explicit_custom_type_int_rowset : table_creator_base 1194 { 1195 test_enum_with_explicit_custom_type_int_rowset(soci::session & sql) 1196 : table_creator_base(sql) 1197 , msession(sql) 1198 { 1199 1200 try 1201 { 1202 sql << "CREATE TABLE soci_test( Type smallint)"; 1203 ; 1204 } 1205 catch (...) 1206 { 1207 drop(); 1208 } 1209 1210 } 1211 ~test_enum_with_explicit_custom_type_int_rowset() 1212 { 1213 drop(); 1214 } 1215 1216 private: 1217 void drop() 1218 { 1219 try 1220 { 1221 msession << "drop table if exists soci_test;"; 1222 } 1223 catch (soci_error const& e){ 1224 std::cerr << e.what() << std::endl; 1225 } 1226 } 1227 soci::session& msession; 1228 }; 1229 1230 TEST_CASE("test_enum_with_explicit_custom_type_int_rowset", "[postgresql][bind-variables]") 1231 { 1232 TestIntEnum test_value = TestIntEnum::VALUE_INT_2; 1233 TestIntEnum type_value; 1234 1235 { 1236 soci::session sql(backEnd, connectString); 1237 test_enum_with_explicit_custom_type_int_rowset tableCreator(sql); 1238 1239 statement s1 = (sql.prepare << "insert into soci_test(Type) values(:val)", use(test_value, "val")); 1240 statement s2 = (sql.prepare << "SELECT Type FROM soci_test ;"); 1241 1242 s1.execute(false); 1243 1244 soci::row result; 1245 s2.define_and_bind(); 1246 s2.exchange_for_rowset(soci::into(result)); 1247 s2.execute(true); 1248 1249 type_value = result.get<TestIntEnum>("type"); 1250 } 1251 1252 CHECK(type_value==TestIntEnum::VALUE_INT_2); 1253 } 1254 1255 TEST_CASE("test_enum_with_explicit_custom_type_int_into", "[postgresql][bind-variables]") 1256 { 1257 TestIntEnum test_value = TestIntEnum::VALUE_INT_2; 1258 TestIntEnum type_value; 1259 1260 { 1261 soci::session sql(backEnd, connectString); 1262 test_enum_with_explicit_custom_type_int_rowset tableCreator(sql); 1263 1264 statement s1 = (sql.prepare << "insert into soci_test(Type) values(:val)", use(test_value, "val")); 1265 statement s2 = (sql.prepare << "SELECT Type FROM soci_test ;", into(type_value)); 1266 1267 s1.execute(false); 1268 s2.execute(true); 1269 } 1270 1271 CHECK(type_value==TestIntEnum::VALUE_INT_2); 1272 } 1273 1274 // false_bind_variable_inside_identifier 1275 struct table_creator_colon_in_double_quotes_in_single_quotes : 1276 table_creator_base 1277 { 1278 table_creator_colon_in_double_quotes_in_single_quotes(soci::session & sql) 1279 : table_creator_base(sql) 1280 { 1281 sql << "CREATE TABLE soci_test( \"column_with:colon\" text)"; 1282 } 1283 1284 }; 1285 TEST_CASE("colon_in_double_quotes_in_single_quotes", 1286 "[postgresql][bind-variables]") 1287 { 1288 std::string return_value; 1289 1290 { 1291 soci::session sql(backEnd, connectString); 1292 table_creator_colon_in_double_quotes_in_single_quotes 1293 tableCreator(sql); 1294 1295 sql << "insert into soci_test(\"column_with:colon\") values('hello " 1296 "it is \"10:10\"')"; 1297 sql << "SELECT \"column_with:colon\" from soci_test ;", into 1298 (return_value); 1299 } 1300 1301 CHECK(return_value == "hello it is \"10:10\""); 1302 } 1303 1304 // 1305 // Support for soci Common Tests 1306 // 1307 1308 // DDL Creation objects for common tests 1309 struct table_creator_one : public table_creator_base 1310 { 1311 table_creator_one(soci::session & sql) 1312 : table_creator_base(sql) 1313 { 1314 sql << "create table soci_test(id integer, val integer, c char, " 1315 "str varchar(20), sh int2, ul numeric(20), d float8, " 1316 "num76 numeric(7,6), " 1317 "tm timestamp, i1 integer, i2 integer, i3 integer, " 1318 "name varchar(20))"; 1319 } 1320 }; 1321 1322 struct table_creator_two : public table_creator_base 1323 { 1324 table_creator_two(soci::session & sql) 1325 : table_creator_base(sql) 1326 { 1327 sql << "create table soci_test(num_float float8, num_int integer," 1328 " name varchar(20), sometime timestamp, chr char)"; 1329 } 1330 }; 1331 1332 struct table_creator_three : public table_creator_base 1333 { 1334 table_creator_three(soci::session & sql) 1335 : table_creator_base(sql) 1336 { 1337 sql << "create table soci_test(name varchar(100) not null, " 1338 "phone varchar(15))"; 1339 } 1340 }; 1341 1342 struct table_creator_for_get_affected_rows : table_creator_base 1343 { 1344 table_creator_for_get_affected_rows(soci::session & sql) 1345 : table_creator_base(sql) 1346 { 1347 sql << "create table soci_test(val integer)"; 1348 } 1349 }; 1350 1351 struct table_creator_for_xml : table_creator_base 1352 { 1353 table_creator_for_xml(soci::session& sql) 1354 : table_creator_base(sql) 1355 { 1356 sql << "create table soci_test(id integer, x xml)"; 1357 } 1358 }; 1359 1360 struct table_creator_for_clob : table_creator_base 1361 { 1362 table_creator_for_clob(soci::session& sql) 1363 : table_creator_base(sql) 1364 { 1365 sql << "create table soci_test(id integer, s text)"; 1366 } 1367 }; 1368 1369 // Common tests context 1370 class test_context : public test_context_base 1371 { 1372 public: 1373 test_context(backend_factory const &backend, std::string const &connstr) 1374 : test_context_base(backend, connstr) 1375 {} 1376 1377 table_creator_base* table_creator_1(soci::session& s) const override 1378 { 1379 return new table_creator_one(s); 1380 } 1381 1382 table_creator_base* table_creator_2(soci::session& s) const override 1383 { 1384 return new table_creator_two(s); 1385 } 1386 1387 table_creator_base* table_creator_3(soci::session& s) const override 1388 { 1389 return new table_creator_three(s); 1390 } 1391 1392 table_creator_base* table_creator_4(soci::session& s) const override 1393 { 1394 return new table_creator_for_get_affected_rows(s); 1395 } 1396 1397 table_creator_base* table_creator_xml(soci::session& s) const override 1398 { 1399 return new table_creator_for_xml(s); 1400 } 1401 1402 table_creator_base* table_creator_clob(soci::session& s) const override 1403 { 1404 return new table_creator_for_clob(s); 1405 } 1406 1407 bool has_real_xml_support() const override 1408 { 1409 return true; 1410 } 1411 1412 std::string to_date_time(std::string const &datdt_string) const override 1413 { 1414 return "timestamptz(\'" + datdt_string + "\')"; 1415 } 1416 1417 bool has_fp_bug() const override 1418 { 1419 return false; 1420 } 1421 1422 std::string sql_length(std::string const& s) const override 1423 { 1424 return "char_length(" + s + ")"; 1425 } 1426 }; 1427 1428 int main(int argc, char** argv) 1429 { 1430 1431 #ifdef _MSC_VER 1432 // Redirect errors, unrecoverable problems, and assert() failures to STDERR, 1433 // instead of debug message window. 1434 // This hack is required to run assert()-driven tests by Buildbot. 1435 // NOTE: Comment this 2 lines for debugging with Visual C++ debugger to catch assertions inside. 1436 _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE); 1437 _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR); 1438 #endif //_MSC_VER 1439 1440 if (argc >= 2) 1441 { 1442 connectString = argv[1]; 1443 1444 // Replace the connect string with the process name to ensure that 1445 // CATCH uses the correct name in its messages. 1446 argv[1] = argv[0]; 1447 1448 argc--; 1449 argv++; 1450 } 1451 else 1452 { 1453 std::cout << "usage: " << argv[0] 1454 << " connectstring [test-arguments...]\n" 1455 << "example: " << argv[0] 1456 << " \'connect_string_for_PostgreSQL\'\n"; 1457 return EXIT_FAILURE; 1458 } 1459 1460 test_context tc(backEnd, connectString); 1461 1462 return Catch::Session().run(argc, argv); 1463 }