diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index c1d1b6b2db..ac4ac5e978 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -1249,6 +1249,33 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
+
+ tcp_user_timeout
+
+
+ Define a wrapper for TCP_USER_TIMEOUT socket option of libpq connection.
+
+
+ Specifies the number of milliseconds after which a TCP connection can be
+ aborted by the operation system due to network problems when the data is
+ transmitting through this connection (sending/receiving). A value of 0 uses
+ the system default. This parameter is supported only on systems that support
+ TCP_USER_TIMEOUT or an equivalent socket option, and on Windows; on other
+ systems, it must be zero. In sessions connected via a Unix-domain socket,
+ this parameter is ignored and always reads as zero.
+
+
+
+ This parameter is not supported on Windows, and must be zero.
+
+
+ To enable full control under TCP connection use this option together with
+ keepalive.
+
+
+
+
+
tty
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index c96a52bb1b..bc0baf820c 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -325,6 +325,11 @@ static const internalPQconninfoOption PQconninfoOptions[] = {
"Target-Session-Attrs", "", 11, /* sizeof("read-write") = 11 */
offsetof(struct pg_conn, target_session_attrs)},
+ /* TCP USER TIMEOUT */
+ {"tcp_user_timeout", NULL, NULL, NULL,
+ "TCP_user_timeout", "", 10, /* strlen(INT32_MAX) == 10 */
+ offsetof(struct pg_conn, pgtcp_user_timeout)},
+
/* Terminating entry --- MUST BE LAST */
{NULL, NULL, NULL, NULL,
NULL, NULL, 0}
@@ -1782,6 +1787,40 @@ setKeepalivesWin32(PGconn *conn)
#endif /* SIO_KEEPALIVE_VALS */
#endif /* WIN32 */
+/*
+ * Set the TCP user timeout.
+ */
+static int
+setTCPUserTimeout(PGconn *conn)
+{
+ int timeout;
+
+ if (conn->pgtcp_user_timeout == NULL)
+ return 1;
+
+ if (!parse_int_param(conn->pgtcp_user_timeout,
+ &timeout, conn, "tcp_user_timeout"))
+ return 0;
+
+ if (timeout < 0)
+ timeout = 0;
+
+#ifdef TCP_USER_TIMEOUT
+ if (setsockopt(conn->sock, IPPROTO_TCP, 18,
+ (char *) &timeout, sizeof(timeout)) < 0 && errno != ENOPROTOOPT)
+ {
+ char sebuf[256];
+
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("setsockopt(TCP_USER_TIMEOUT) failed: %s\n"),
+ SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
+ return 0;
+ }
+#endif
+
+ return 1;
+}
+
/* ----------
* connectDBStart -
* Begin the process of making a connection to the backend.
@@ -2373,6 +2412,17 @@ keep_going: /* We will come back to here until there is
goto keep_going;
}
+ if (!IS_AF_UNIX(addr_cur->ai_family))
+ {
+ if (!setTCPUserTimeout(conn))
+ {
+ closesocket(conn->sock);
+ conn->sock = -1;
+ conn->addr_cur = addr_cur->ai_next;
+ goto keep_going;
+ }
+ }
+
#ifdef F_SETFD
if (fcntl(conn->sock, F_SETFD, FD_CLOEXEC) == -1)
{
@@ -3651,6 +3701,8 @@ freePGconn(PGconn *conn)
free(conn->pgtty);
if (conn->connect_timeout)
free(conn->connect_timeout);
+ if (conn->pgtcp_user_timeout)
+ free(conn->pgtcp_user_timeout);
if (conn->pgoptions)
free(conn->pgoptions);
if (conn->appname)
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 4a93d8edbc..729342c4cc 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -336,6 +336,7 @@ struct pg_conn
char *pgtty; /* tty on which the backend messages is
* displayed (OBSOLETE, NOT USED) */
char *connect_timeout; /* connection timeout (numeric string) */
+ char *pgtcp_user_timeout; /* TCP user timeout (numeric string) */
char *client_encoding_initial; /* encoding to use */
char *pgoptions; /* options to start the backend with */
char *appname; /* application name */
@@ -351,6 +352,7 @@ struct pg_conn
* retransmits */
char *keepalives_count; /* maximum number of TCP keepalive
* retransmits */
+ char *tcp_user_timeout; /* TCP USER TIMEOUT */
char *sslmode; /* SSL mode (require,prefer,allow,disable) */
char *sslcompression; /* SSL compression (0 or 1) */
char *sslkey; /* client key filename */