diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml index c1d1b6b2db..32f95360f2 100644 --- a/doc/src/sgml/libpq.sgml +++ b/doc/src/sgml/libpq.sgml @@ -1249,6 +1249,27 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname + + socket_timeout + + + Number of seconds to wait for socket read/write operation before + closing the connection, as a decimal integer. This can be used as a + stop-gap timeout mean. A value of zero (the default) turns this off, + which means wait indefinitely. The minimum allowed timeout is + 2 seconds, so a value of 1 is interpreted as 2. + + + + It is recommended to set greater than the value of other timeout paramters, + connect_timeout, statement_timeout, + TCP_KEEP_ALIVES, TCP_USER_TIMEOUT + because setting smaller value can cause not desirable disconnection. + + + + + tty diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c index e3bf6a7449..df8d7fe6a9 100644 --- a/src/interfaces/libpq/fe-connect.c +++ b/src/interfaces/libpq/fe-connect.c @@ -208,6 +208,10 @@ static const internalPQconninfoOption PQconninfoOptions[] = { "Connect-timeout", "", 10, /* strlen(INT32_MAX) == 10 */ offsetof(struct pg_conn, connect_timeout)}, + {"socket_timeout", NULL, NULL, NULL, + "Socket-timeout", "", 10, /* strlen(INT32_MAX) == 10 */ + offsetof(struct pg_conn, pgsocket_timeout)}, + {"dbname", "PGDATABASE", NULL, NULL, "Database-Name", "", 20, offsetof(struct pg_conn, dbName)}, @@ -405,6 +409,8 @@ static char *passwordFromFile(const char *hostname, const char *port, const char const char *username, const char *pgpassfile); static void pgpassfileWarning(PGconn *conn); static void default_threadlock(int acquire); +static bool parse_int_param(const char *value, int *result, PGconn *conn, + const char *context); /* global variable because fe-auth.c needs to access it */ @@ -1238,6 +1244,22 @@ connectOptions2(PGconn *conn) goto oom_error; } + if (conn->pgsocket_timeout) + { + if (parse_int_param(conn->pgsocket_timeout, + &conn->socket_timeout, conn, "socket_timeout")) + { + /* + * Rounding could cause communication to fail; + * insist on at least two seconds. + */ + if(conn->socket_timeout > 0 && conn->socket_timeout < 2) + conn->socket_timeout = 2; + } + else + goto iiv_error; + } + /* * Validate target_session_attrs option. */ @@ -1268,6 +1290,12 @@ oom_error: printfPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory\n")); return false; + +iiv_error: + conn->status = CONNECTION_BAD; + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("invalid integer value for socket_timeout\n")); + return false; } /* @@ -3655,6 +3683,8 @@ freePGconn(PGconn *conn) free(conn->pgtty); if (conn->connect_timeout) free(conn->connect_timeout); + if (conn->pgsocket_timeout) + free(conn->pgsocket_timeout); if (conn->pgoptions) free(conn->pgoptions); if (conn->appname) diff --git a/src/interfaces/libpq/fe-misc.c b/src/interfaces/libpq/fe-misc.c index ea4c9d2ee0..633430b54e 100644 --- a/src/interfaces/libpq/fe-misc.c +++ b/src/interfaces/libpq/fe-misc.c @@ -1020,6 +1020,32 @@ pqFlush(PGconn *conn) int pqWait(int forRead, int forWrite, PGconn *conn) { + int result; + + if (conn->socket_timeout > 0) + { + time_t finish_time; + + finish_time = time(NULL) + conn->socket_timeout; + result = pqSocketCheck(conn, forRead, forWrite, finish_time); + + if (result < 0) + return EOF; + + if (result == 0) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("timeout expired\n")); + conn->status = CONNECTION_BAD; + pqsecure_close(conn); + closesocket(conn->sock); + conn->sock = -1; + return EOF; + } + + return 0; + } + return pqWaitTimed(forRead, forWrite, conn, (time_t) -1); } diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h index dbe0f7e5c0..5addc2e746 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 *pgsocket_timeout; /* socket timeout (numeric string) */ char *client_encoding_initial; /* encoding to use */ char *pgoptions; /* options to start the backend with */ char *appname; /* application name */ @@ -502,6 +503,7 @@ struct pg_conn /* Buffer for receiving various parts of messages */ PQExpBufferData workBuffer; /* expansible string */ + int socket_timeout; /* socket timeout (numeric)*/ }; /* PGcancel stores all data necessary to cancel a connection. A copy of this