doc PG 17 relnotes: add FETCH_COUNT item
[pgsql.git] / src / fe_utils / cancel.c
blobdcff9a85641846b3829e61c329dfd0518c1e983e
1 /*------------------------------------------------------------------------
3 * Query cancellation support for frontend code
5 * Assorted utility functions to control query cancellation with signal
6 * handler for SIGINT.
9 * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
10 * Portions Copyright (c) 1994, Regents of the University of California
12 * src/fe_utils/cancel.c
14 *------------------------------------------------------------------------
17 #include "postgres_fe.h"
19 #include <unistd.h>
21 #include "common/connect.h"
22 #include "fe_utils/cancel.h"
23 #include "fe_utils/string_utils.h"
27 * Write a simple string to stderr --- must be safe in a signal handler.
28 * We ignore the write() result since there's not much we could do about it.
29 * Certain compilers make that harder than it ought to be.
31 #define write_stderr(str) \
32 do { \
33 const char *str_ = (str); \
34 int rc_; \
35 rc_ = write(fileno(stderr), str_, strlen(str_)); \
36 (void) rc_; \
37 } while (0)
40 * Contains all the information needed to cancel a query issued from
41 * a database connection to the backend.
43 static PGcancel *volatile cancelConn = NULL;
46 * Predetermined localized error strings --- needed to avoid trying
47 * to call gettext() from a signal handler.
49 static const char *cancel_sent_msg = NULL;
50 static const char *cancel_not_sent_msg = NULL;
53 * CancelRequested is set when we receive SIGINT (or local equivalent).
54 * There is no provision in this module for resetting it; but applications
55 * might choose to clear it after successfully recovering from a cancel.
56 * Note that there is no guarantee that we successfully sent a Cancel request,
57 * or that the request will have any effect if we did send it.
59 volatile sig_atomic_t CancelRequested = false;
61 #ifdef WIN32
62 static CRITICAL_SECTION cancelConnLock;
63 #endif
66 * Additional callback for cancellations.
68 static void (*cancel_callback) (void) = NULL;
72 * SetCancelConn
74 * Set cancelConn to point to the current database connection.
76 void
77 SetCancelConn(PGconn *conn)
79 PGcancel *oldCancelConn;
81 #ifdef WIN32
82 EnterCriticalSection(&cancelConnLock);
83 #endif
85 /* Free the old one if we have one */
86 oldCancelConn = cancelConn;
88 /* be sure handle_sigint doesn't use pointer while freeing */
89 cancelConn = NULL;
91 if (oldCancelConn != NULL)
92 PQfreeCancel(oldCancelConn);
94 cancelConn = PQgetCancel(conn);
96 #ifdef WIN32
97 LeaveCriticalSection(&cancelConnLock);
98 #endif
102 * ResetCancelConn
104 * Free the current cancel connection, if any, and set to NULL.
106 void
107 ResetCancelConn(void)
109 PGcancel *oldCancelConn;
111 #ifdef WIN32
112 EnterCriticalSection(&cancelConnLock);
113 #endif
115 oldCancelConn = cancelConn;
117 /* be sure handle_sigint doesn't use pointer while freeing */
118 cancelConn = NULL;
120 if (oldCancelConn != NULL)
121 PQfreeCancel(oldCancelConn);
123 #ifdef WIN32
124 LeaveCriticalSection(&cancelConnLock);
125 #endif
130 * Code to support query cancellation
132 * Note that sending the cancel directly from the signal handler is safe
133 * because PQcancel() is written to make it so. We use write() to report
134 * to stderr because it's better to use simple facilities in a signal
135 * handler.
137 * On Windows, the signal canceling happens on a separate thread, because
138 * that's how SetConsoleCtrlHandler works. The PQcancel function is safe
139 * for this (unlike PQrequestCancel). However, a CRITICAL_SECTION is required
140 * to protect the PGcancel structure against being changed while the signal
141 * thread is using it.
144 #ifndef WIN32
147 * handle_sigint
149 * Handle interrupt signals by canceling the current command, if cancelConn
150 * is set.
152 static void
153 handle_sigint(SIGNAL_ARGS)
155 char errbuf[256];
157 CancelRequested = true;
159 if (cancel_callback != NULL)
160 cancel_callback();
162 /* Send QueryCancel if we are processing a database query */
163 if (cancelConn != NULL)
165 if (PQcancel(cancelConn, errbuf, sizeof(errbuf)))
167 write_stderr(cancel_sent_msg);
169 else
171 write_stderr(cancel_not_sent_msg);
172 write_stderr(errbuf);
178 * setup_cancel_handler
180 * Register query cancellation callback for SIGINT.
182 void
183 setup_cancel_handler(void (*query_cancel_callback) (void))
185 cancel_callback = query_cancel_callback;
186 cancel_sent_msg = _("Cancel request sent\n");
187 cancel_not_sent_msg = _("Could not send cancel request: ");
189 pqsignal(SIGINT, handle_sigint);
192 #else /* WIN32 */
194 static BOOL WINAPI
195 consoleHandler(DWORD dwCtrlType)
197 char errbuf[256];
199 if (dwCtrlType == CTRL_C_EVENT ||
200 dwCtrlType == CTRL_BREAK_EVENT)
202 CancelRequested = true;
204 if (cancel_callback != NULL)
205 cancel_callback();
207 /* Send QueryCancel if we are processing a database query */
208 EnterCriticalSection(&cancelConnLock);
209 if (cancelConn != NULL)
211 if (PQcancel(cancelConn, errbuf, sizeof(errbuf)))
213 write_stderr(cancel_sent_msg);
215 else
217 write_stderr(cancel_not_sent_msg);
218 write_stderr(errbuf);
222 LeaveCriticalSection(&cancelConnLock);
224 return TRUE;
226 else
227 /* Return FALSE for any signals not being handled */
228 return FALSE;
231 void
232 setup_cancel_handler(void (*callback) (void))
234 cancel_callback = callback;
235 cancel_sent_msg = _("Cancel request sent\n");
236 cancel_not_sent_msg = _("Could not send cancel request: ");
238 InitializeCriticalSection(&cancelConnLock);
240 SetConsoleCtrlHandler(consoleHandler, TRUE);
243 #endif /* WIN32 */