mirror of
https://github.com/SOCI/soci.git
synced 2026-02-13 09:08:46 -06:00
Merge branch 'pg-timeout'
Add support for TCP timeout in PostgreSQL backend under Windows too. See #1275.
This commit is contained in:
@@ -62,6 +62,8 @@ Note that in the single-row operation:
|
||||
* bulk queries are not supported, and
|
||||
* in order to fulfill the expectations of the underlying client library, the complete rowset has to be exhausted before executing further queries on the same session.
|
||||
|
||||
Another parameter is handled specially: while libpq client library supports `tcp_user_timeout` parameter, it only provides support for it under Linux. SOCI also handles this parameter, which can change the time before a broken connection times out (which depends on the system settings but is typically relatively long), under Windows. It handles it in the same way as libpq itself, i.e. the value of this parameter is expressed in milliseconds, but because Windows sockets only use second granularity, SOCI will round the value to the nearest second. In particular, this means that this parameter must be greater than or equal to 500 to be accepted.
|
||||
|
||||
Once you have created a `session` object as shown above, you can use it to access the database, for example:
|
||||
|
||||
```cpp
|
||||
|
||||
@@ -31,6 +31,11 @@ if (NOT SOCI_POSTGRESQL)
|
||||
return()
|
||||
endif()
|
||||
|
||||
if (WIN32)
|
||||
# We use WinSock functions under Windows.
|
||||
target_link_libraries(soci_postgresql PUBLIC ws2_32)
|
||||
endif()
|
||||
|
||||
option(SOCI_POSTGRESQL_NO_LO64
|
||||
"Do not use lo_xxx64() functions for compatibility with PostgreSQL < 9.3"
|
||||
OFF
|
||||
|
||||
@@ -19,6 +19,33 @@
|
||||
#include <ctime>
|
||||
#include <sstream>
|
||||
|
||||
// We have 3 cases of handling tcp_user_timeout option:
|
||||
//
|
||||
// - Windows: libpq doesn't handle it at all in all the current versions, but
|
||||
// we may implement support for it ourselves, see below.
|
||||
// - Linux (or, to be optimistic, any other system defining TCP_USER_TIMEOUT):
|
||||
// libpq supports it since v12, so we don't have anything to do.
|
||||
// - Other: there is no support for it in libpq and we can't implement it
|
||||
// (although macOS has TCP_RXT_CONNDROPTIME which seems similar and we could
|
||||
// try using it if there is interest in it).
|
||||
//
|
||||
// Currently we don't differentiate between the last 2 cases as we don't have
|
||||
// any fallback implementation anyhow, even if, in theory, we could switch to
|
||||
// using async libpq API to implement it on all platforms, so we only check for
|
||||
// Windows.
|
||||
#ifdef _WIN32
|
||||
// Include the headers we need for setsockopt(TCP_MAXRT) used below.
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
|
||||
// Some old MinGW versions don't define TCP_MAXRT, do it ourselves.
|
||||
#ifndef TCP_MAXRT
|
||||
#define TCP_MAXRT 5
|
||||
#endif
|
||||
|
||||
#include "soci-cstrtoi.h"
|
||||
#endif
|
||||
|
||||
using namespace soci;
|
||||
using namespace soci::details;
|
||||
|
||||
@@ -204,6 +231,53 @@ void postgresql_session_backend::connect(
|
||||
throw soci_error(msg);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
// As explained above, implement our own support for this option under
|
||||
// Windows.
|
||||
std::string timeoutStr;
|
||||
if (params.get_option("tcp_user_timeout", timeoutStr))
|
||||
{
|
||||
int timeoutMs = 0;
|
||||
if (!cstring_to_integer(timeoutMs, timeoutStr.c_str()))
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << "Invalid value for tcp_user_timeout option: \""
|
||||
<< timeoutStr << "\".";
|
||||
PQfinish(conn);
|
||||
|
||||
throw soci_error(oss.str());
|
||||
}
|
||||
|
||||
// The value is in milliseconds, but we need to convert it to seconds,
|
||||
// prefer to round it rather than truncate.
|
||||
DWORD const timeoutSec = (timeoutMs + 500) / 1000;
|
||||
|
||||
// It's not clear what does the timeout of 0 mean, so ignore it.
|
||||
if (!timeoutSec)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << "Invalid value for tcp_user_timeout option: "
|
||||
<< timeoutMs << "ms. It must be greater than 1s.";
|
||||
PQfinish(conn);
|
||||
|
||||
throw soci_error(oss.str());
|
||||
}
|
||||
|
||||
int const sock = PQsocket(conn);
|
||||
if (setsockopt(sock, IPPROTO_TCP, TCP_MAXRT,
|
||||
reinterpret_cast<char const*>(&timeoutSec),
|
||||
sizeof(timeoutSec)) != 0)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << "Failed to set TCP_MAXRT option on the socket: WinSock error "
|
||||
<< WSAGetLastError() << ".";
|
||||
PQfinish(conn);
|
||||
|
||||
throw soci_error(oss.str());
|
||||
}
|
||||
}
|
||||
#endif // _WIN32
|
||||
|
||||
// Increase the number of digits used for floating point values to ensure
|
||||
// that the conversions to/from text round trip correctly, which is not the
|
||||
// case with the default value of 0. Use the maximal supported value, which
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#include <catch.hpp>
|
||||
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
|
||||
#include "test-context.h"
|
||||
@@ -44,6 +45,9 @@ TEST_CASE_METHOD(common_tests, "Reconnect", "[keep-alive][.]")
|
||||
"and press Enter" << std::endl;
|
||||
std::cin.get();
|
||||
|
||||
using Clock = std::chrono::steady_clock;
|
||||
auto const start = Clock::now();
|
||||
|
||||
try
|
||||
{
|
||||
CHECK( !sql.is_connected() );
|
||||
@@ -69,7 +73,11 @@ TEST_CASE_METHOD(common_tests, "Reconnect", "[keep-alive][.]")
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "Please undo the previous action "
|
||||
auto const elapsed = Clock::now() - start;
|
||||
std::cout << "Database query failed after "
|
||||
<< std::chrono::duration_cast<std::chrono::seconds>(elapsed).count()
|
||||
<< " seconds as expected.\n"
|
||||
<< "Now please undo the previous action "
|
||||
"(restart the server, plug the cable back, ...) "
|
||||
"and press Enter" << std::endl;
|
||||
std::cin.get();
|
||||
|
||||
Reference in New Issue
Block a user