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 }