diff options
author | Alvaro Herrera <alvherre@alvh.no-ip.org> | 2024-03-28 11:31:03 +0100 |
---|---|---|
committer | Alvaro Herrera <alvherre@alvh.no-ip.org> | 2024-03-28 11:31:03 +0100 |
commit | 2466d6654f85d7ec136d09d52ae22771452a19df (patch) | |
tree | 5e68b65e65d2f041318daf327fe94bdc15be6f8d /src | |
parent | 427005742bd2efdcee0f361e17d1a76664ff001b (diff) | |
download | postgresql-2466d6654f85d7ec136d09d52ae22771452a19df.tar.gz postgresql-2466d6654f85d7ec136d09d52ae22771452a19df.zip |
libpq-be-fe-helpers.h: wrap new cancel APIs
Commit 61461a300c1c introduced new functions to libpq for cancelling
queries. This commit introduces a helper function that backend-side
libraries and extensions can use to invoke those. This function takes a
timeout and can itself be interrupted while it is waiting for a cancel
request to be sent and processed, instead of being blocked.
This replaces the usage of the old functions in postgres_fdw and dblink.
Finally, it also adds some test coverage for the cancel support in
postgres_fdw.
Author: Jelte Fennema-Nio <postgres@jeltef.nl>
Discussion: https://postgr.es/m/CAGECzQT_VgOWWENUqvUV9xQmbaCyXjtRRAYO8W07oqashk_N+g@mail.gmail.com
Diffstat (limited to 'src')
-rw-r--r-- | src/include/libpq/libpq-be-fe-helpers.h | 89 |
1 files changed, 89 insertions, 0 deletions
diff --git a/src/include/libpq/libpq-be-fe-helpers.h b/src/include/libpq/libpq-be-fe-helpers.h index 5d33bcf32f7..2adf92030af 100644 --- a/src/include/libpq/libpq-be-fe-helpers.h +++ b/src/include/libpq/libpq-be-fe-helpers.h @@ -44,6 +44,8 @@ #include "miscadmin.h" #include "storage/fd.h" #include "storage/latch.h" +#include "utils/timestamp.h" +#include "utils/wait_event.h" static inline void libpqsrv_connect_prepare(void); @@ -365,4 +367,91 @@ libpqsrv_get_result(PGconn *conn, uint32 wait_event_info) return PQgetResult(conn); } +/* + * Submit a cancel request to the given connection, waiting only until + * the given time. + * + * We sleep interruptibly until we receive confirmation that the cancel + * request has been accepted, and if it is, return NULL; if the cancel + * request fails, return an error message string (which is not to be + * freed). + * + * For other problems (to wit: OOM when strdup'ing an error message from + * libpq), this function can ereport(ERROR). + * + * Note: this function leaks a string's worth of memory when reporting + * libpq errors. Make sure to call it in a transient memory context. + */ +static inline char * +libpqsrv_cancel(PGconn *conn, TimestampTz endtime) +{ + PGcancelConn *cancel_conn; + char *error = NULL; + + cancel_conn = PQcancelCreate(conn); + if (cancel_conn == NULL) + return _("out of memory"); + + /* In what follows, do not leak any PGcancelConn on any errors. */ + + PG_TRY(); + { + if (!PQcancelStart(cancel_conn)) + { + error = pchomp(PQcancelErrorMessage(cancel_conn)); + goto exit; + } + + for (;;) + { + PostgresPollingStatusType pollres; + TimestampTz now; + long cur_timeout; + int waitEvents = WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH; + + pollres = PQcancelPoll(cancel_conn); + if (pollres == PGRES_POLLING_OK) + break; /* success! */ + + /* If timeout has expired, give up, else get sleep time. */ + now = GetCurrentTimestamp(); + cur_timeout = TimestampDifferenceMilliseconds(now, endtime); + if (cur_timeout <= 0) + { + error = _("cancel request timed out"); + break; + } + + switch (pollres) + { + case PGRES_POLLING_READING: + waitEvents |= WL_SOCKET_READABLE; + break; + case PGRES_POLLING_WRITING: + waitEvents |= WL_SOCKET_WRITEABLE; + break; + default: + error = pchomp(PQcancelErrorMessage(cancel_conn)); + goto exit; + } + + /* Sleep until there's something to do */ + WaitLatchOrSocket(MyLatch, waitEvents, PQcancelSocket(cancel_conn), + cur_timeout, PG_WAIT_CLIENT); + + ResetLatch(MyLatch); + + CHECK_FOR_INTERRUPTS(); + } +exit: ; + } + PG_FINALLY(); + { + PQcancelFinish(cancel_conn); + } + PG_END_TRY(); + + return error; +} + #endif /* LIBPQ_BE_FE_HELPERS_H */ |