1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203
|
//
// Copyright (C) 2004-2024 Maciej Sobczak, Stephen Hutton
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// https://www.boost.org/LICENSE_1_0.txt)
//
#include "soci/soci.h"
#include "soci/callbacks.h"
#include <catch.hpp>
#include <iostream>
#include "test-context.h"
namespace soci
{
namespace tests
{
// This variable is referenced from test-common.cpp to force linking this file.
volatile bool soci_use_test_manual = false;
// These tests are disabled by default, as they require manual intevention, but
// can be run by explicitly giving their names on the command line.
// Check if reconnecting to the database after losing connection to it works.
TEST_CASE_METHOD(common_tests, "Reconnect", "[keep-alive][.]")
{
soci::session sql(backEndFactory_, connectString_);
auto_table_creator tableCreator(tc_.table_creator_1(sql));
int id = 17;
sql << "insert into soci_test (id) values (:id)", use(id);
REQUIRE_NOTHROW( sql.commit() );
CHECK( sql.is_connected() );
std::cout << "Please break connection to the database "
"(stop the server, unplug the network cable, ...) "
"and press Enter" << std::endl;
std::cin.get();
try
{
CHECK( !sql.is_connected() );
int id2;
sql << "select id from soci_test", into(id2);
FAIL("Connection to the database still available");
return;
}
catch (soci_error const& e)
{
if ( sql.get_backend_name() == "odbc" ||
e.get_error_category() == soci_error::unknown )
{
WARN( "Skipping error check because ODBC driver returned "
"unknown error: " << e.what() );
}
else
{
INFO( "Exception message: " << e.what() );
CHECK( e.get_error_category() == soci_error::connection_error );
}
}
std::cout << "Please undo the previous action "
"(restart the server, plug the cable back, ...) "
"and press Enter" << std::endl;
std::cin.get();
REQUIRE_NOTHROW( sql.reconnect() );
CHECK( sql.is_connected() );
int id2 = 1234;
sql << "select id from soci_test", into(id2);
CHECK( id2 == id );
}
// Check if automatically reconnecting to the database works.
//
// Note: this test doesn't work at all, failover doesn't happen neither with
// Oracle nor with PostgreSQL (which are the only backends for which it's
// implemented at all) and it's not clear how is it even supposed to work.
TEST_CASE_METHOD(common_tests, "Failover", "[keep-alive][.]")
{
soci::session sql(backEndFactory_, connectString_);
class MyCallback : public soci::failover_callback
{
public:
MyCallback() : attempted_(false), reconnected_(false)
{
}
bool did_reconnect() const { return reconnected_; }
void started() override
{
std::cout << "Please undo the previous action "
"(restart the server, plug the cable back, ...) "
"and press Enter" << std::endl;
std::cin.get();
}
void failed(bool& retry, std::string&) override
{
// We only retry once.
retry = !attempted_;
attempted_ = true;
}
void finished(soci::session&) override
{
reconnected_ = true;
}
void aborted() override
{
FAIL( "Failover aborted" );
}
private:
bool attempted_;
bool reconnected_;
} myCallback;
sql.set_failover_callback(myCallback);
auto_table_creator tableCreator(tc_.table_creator_1(sql));
int id = 17;
sql << "insert into soci_test (id) values (:id)", use(id);
REQUIRE_NOTHROW( sql.commit() );
std::cout << "Please break connection to the database "
"(stop the server, unplug the network cable, ...) "
"and press Enter" << std::endl;
std::cin.get();
int id2;
sql << "select id from soci_test", into(id2);
CHECK( id2 == id );
CHECK( myCallback.did_reconnect() );
}
// This pseudo-test allows to execute an arbitrary SQL query by defining
// SOCI_TEST_SQL environment variable and examine the resulting error.
TEST_CASE_METHOD(common_tests, "Execute", "[.]")
{
auto const text = std::getenv("SOCI_TEST_SQL");
if (!text)
{
FAIL( "SOCI_TEST_SQL environment variable must be set." );
}
soci::session sql(backEndFactory_, connectString_);
try
{
sql << text;
WARN("Statement executed successfully.");
}
catch (soci_error const& e)
{
char const* const categories[] =
{
"connection_error",
"invalid_statement",
"no_privilege",
"no_data",
"constraint_violation",
"unknown_transaction_state",
"system_error",
"unknown"
};
unsigned const cat = e.get_error_category();
REQUIRE(cat < sizeof(categories) / sizeof(categories[0]));
WARN("Statement execution failed: " << e.what() << "\n"
"Error category: " << categories[cat] << "\n");
auto const& backend = e.get_backend_name();
if ( !backend.empty() )
{
if ( e.get_backend_error_code() )
WARN(backend << " error " << e.get_backend_error_code() << "\n");
if ( !e.get_sqlstate().empty() )
WARN(backend << " SQL state " << e.get_sqlstate() << "\n");
}
}
}
} // namespace tests
} // namespace soci
|