From 234b238343cb7e155f57128ee3e3a0ae46e836b9 Mon Sep 17 00:00:00 2001 From: Hayato Kuroda Date: Fri, 27 Jan 2023 03:17:18 +0000 Subject: [PATCH v33 1/3] Add PQconnCheck and PQconnCheckable to libpq PQconnCheck() function allows to check the status of the connection by polling the socket. This function is currently available only on systems that support the non-standard POLLRDHUP extension to the poll system call, including Linux. PQconnCheckable() checks whether the above function is available or not. --- doc/src/sgml/libpq.sgml | 38 ++++++++++++++++ src/interfaces/libpq/exports.txt | 2 + src/interfaces/libpq/fe-misc.c | 78 +++++++++++++++++++++++++------- src/interfaces/libpq/libpq-fe.h | 4 ++ 4 files changed, 106 insertions(+), 16 deletions(-) diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml index 0e7ae70c70..afa1fe6731 100644 --- a/doc/src/sgml/libpq.sgml +++ b/doc/src/sgml/libpq.sgml @@ -2679,6 +2679,44 @@ void *PQgetssl(const PGconn *conn); + + PQconnCheckPQconnCheck + + + Returns the health of the connection. + + +int PQconnCheck(PGconn *conn); + + + + + This function checks the health of the connection. Unlike , + this check is performed by polling the corresponding socket. This + function is currently available only on systems that support the + non-standard POLLRDHUP extension to the poll + system call, including Linux. + returns greater than zero if the remote peer seems to be closed, returns + 0 if the socket is valid, and returns -1 + if the connection has already been closed or an error has occurred. + + + + + + PQconnCheckablePQconnCheckable + + + Returns true (1) or false (0) to indicate if the + function is supported on this platform. + + +int PQconnCheckable(void); + + + + + diff --git a/src/interfaces/libpq/exports.txt b/src/interfaces/libpq/exports.txt index e8bcc88370..a06dea9acd 100644 --- a/src/interfaces/libpq/exports.txt +++ b/src/interfaces/libpq/exports.txt @@ -186,3 +186,5 @@ PQpipelineStatus 183 PQsetTraceFlags 184 PQmblenBounded 185 PQsendFlushRequest 186 +PQconnCheck 187 +PQconnCheckable 188 diff --git a/src/interfaces/libpq/fe-misc.c b/src/interfaces/libpq/fe-misc.c index 3653a1a8a6..1a3478dc7e 100644 --- a/src/interfaces/libpq/fe-misc.c +++ b/src/interfaces/libpq/fe-misc.c @@ -53,9 +53,10 @@ static int pqPutMsgBytes(const void *buf, size_t len, PGconn *conn); static int pqSendSome(PGconn *conn, int len); -static int pqSocketCheck(PGconn *conn, int forRead, int forWrite, - time_t end_time); -static int pqSocketPoll(int sock, int forRead, int forWrite, time_t end_time); +static int pqSocketCheck(PGconn *conn, int forRead, + int forWrite, int forConnCheck, time_t end_time); +static int pqSocketPoll(int sock, int forRead, + int forWrite, int forConnCheck, time_t end_time); /* * PQlibVersion: return the libpq version number @@ -993,7 +994,7 @@ pqWaitTimed(int forRead, int forWrite, PGconn *conn, time_t finish_time) { int result; - result = pqSocketCheck(conn, forRead, forWrite, finish_time); + result = pqSocketCheck(conn, forRead, forWrite, 0, finish_time); if (result < 0) return -1; /* errorMessage is already set */ @@ -1014,7 +1015,7 @@ pqWaitTimed(int forRead, int forWrite, PGconn *conn, time_t finish_time) int pqReadReady(PGconn *conn) { - return pqSocketCheck(conn, 1, 0, (time_t) 0); + return pqSocketCheck(conn, 1, 0, 0, (time_t) 0); } /* @@ -1024,19 +1025,52 @@ pqReadReady(PGconn *conn) int pqWriteReady(PGconn *conn) { - return pqSocketCheck(conn, 0, 1, (time_t) 0); + return pqSocketCheck(conn, 0, 1, 0, (time_t) 0); +} + +/* + * Check whether the socket peer closed connection or not. + * + * Returns >0 if remote peer seems to be closed, 0 if it is valid, + * -1 if the input connection is bad or an error occurred. + */ +int +PQconnCheck(PGconn *conn) +{ + return pqSocketCheck(conn, 0, 0, 1, (time_t) 0); +} + +/* + * Check whether PQconnCheck() can work on this platform. + * + * Returns true (1) if this can use PQconnCheck(), otherwise false (0). + */ +int +PQconnCheckable(void) +{ +#if (defined(HAVE_POLL) && defined(POLLRDHUP)) + return true; +#else + return false; +#endif } /* * Checks a socket, using poll or select, for data to be read, written, - * or both. Returns >0 if one or more conditions are met, 0 if it timed - * out, -1 if an error occurred. + * or both. Moreover, this function can check the health of socket on some + * limited platforms if end_time is 0. + * + * Returns >0 if one or more conditions are met, 0 if it timed out, -1 if an + * error occurred. Note that if 0 is returned and forConnCheck is requested, it + * means that the socket has not matched POLLRDHUP event and the socket has + * still survived. * * If SSL is in use, the SSL buffer is checked prior to checking the socket * for read data directly. */ static int -pqSocketCheck(PGconn *conn, int forRead, int forWrite, time_t end_time) +pqSocketCheck(PGconn *conn, int forRead, + int forWrite, int forConnCheck, time_t end_time) { int result; @@ -1059,7 +1093,7 @@ pqSocketCheck(PGconn *conn, int forRead, int forWrite, time_t end_time) /* We will retry as long as we get EINTR */ do - result = pqSocketPoll(conn->sock, forRead, forWrite, end_time); + result = pqSocketPoll(conn->sock, forRead, forWrite, forConnCheck, end_time); while (result < 0 && SOCK_ERRNO == EINTR); if (result < 0) @@ -1076,15 +1110,20 @@ pqSocketCheck(PGconn *conn, int forRead, int forWrite, time_t end_time) /* * Check a file descriptor for read and/or write data, possibly waiting. - * If neither forRead nor forWrite are set, immediately return a timeout - * condition (without waiting). Return >0 if condition is met, 0 - * if a timeout occurred, -1 if an error or interrupt occurred. + * Moreover, this function can check the health of socket on some limited + * platforms if end_time is 0. + * + * If neither forRead, forWrite nor forConnCheck are set, immediately return a + * timeout condition (without waiting). Return >0 if condition is met, 0 if a + * timeout occurred, -1 if an error or interrupt occurred. Note that if 0 is + * returned and forConnCheck is requested, it means that the socket has not + * matched POLLRDHUP event and the socket has still survived. * * Timeout is infinite if end_time is -1. Timeout is immediate (no blocking) * if end_time is 0 (or indeed, any time before now). */ static int -pqSocketPoll(int sock, int forRead, int forWrite, time_t end_time) +pqSocketPoll(int sock, int forRead, int forWrite, int forConnCheck, time_t end_time) { /* We use poll(2) if available, otherwise select(2) */ #ifdef HAVE_POLL @@ -1092,7 +1131,10 @@ pqSocketPoll(int sock, int forRead, int forWrite, time_t end_time) int timeout_ms; if (!forRead && !forWrite) - return 0; + { + if (!forConnCheck || !PQconnCheckable() || end_time != 0) + return 0; + } input_fd.fd = sock; input_fd.events = POLLERR; @@ -1102,6 +1144,11 @@ pqSocketPoll(int sock, int forRead, int forWrite, time_t end_time) input_fd.events |= POLLIN; if (forWrite) input_fd.events |= POLLOUT; +#if defined(POLLRDHUP) + if (forConnCheck) + input_fd.events |= POLLRDHUP; +#endif + /* Compute appropriate timeout interval */ if (end_time == ((time_t) -1)) @@ -1218,7 +1265,6 @@ PQenv2encoding(void) return encoding; } - #ifdef ENABLE_NLS static void diff --git a/src/interfaces/libpq/libpq-fe.h b/src/interfaces/libpq/libpq-fe.h index f3d9220496..e1bd0cd7b7 100644 --- a/src/interfaces/libpq/libpq-fe.h +++ b/src/interfaces/libpq/libpq-fe.h @@ -648,6 +648,10 @@ extern int PQdsplen(const char *s, int encoding); /* Get encoding id from environment variable PGCLIENTENCODING */ extern int PQenv2encoding(void); +/* Check whether the postgres server is still alive or not */ +extern int PQconnCheck(PGconn *conn); +extern int PQconnCheckable(void); + /* === in fe-auth.c === */ extern char *PQencryptPassword(const char *passwd, const char *user); -- 2.27.0