1 /*------------------------------------------------------------------------
3 * Query cancellation support for frontend code
5 * Assorted utility functions to control query cancellation with signal
9 * Portions Copyright (c) 1996-2025, 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"
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) \
33 const char *str_ = (str); \
35 rc_ = write(fileno(stderr), str_, strlen(str_)); \
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;
62 static CRITICAL_SECTION cancelConnLock
;
66 * Additional callback for cancellations.
68 static void (*cancel_callback
) (void) = NULL
;
74 * Set cancelConn to point to the current database connection.
77 SetCancelConn(PGconn
*conn
)
79 PGcancel
*oldCancelConn
;
82 EnterCriticalSection(&cancelConnLock
);
85 /* Free the old one if we have one */
86 oldCancelConn
= cancelConn
;
88 /* be sure handle_sigint doesn't use pointer while freeing */
91 if (oldCancelConn
!= NULL
)
92 PQfreeCancel(oldCancelConn
);
94 cancelConn
= PQgetCancel(conn
);
97 LeaveCriticalSection(&cancelConnLock
);
104 * Free the current cancel connection, if any, and set to NULL.
107 ResetCancelConn(void)
109 PGcancel
*oldCancelConn
;
112 EnterCriticalSection(&cancelConnLock
);
115 oldCancelConn
= cancelConn
;
117 /* be sure handle_sigint doesn't use pointer while freeing */
120 if (oldCancelConn
!= NULL
)
121 PQfreeCancel(oldCancelConn
);
124 LeaveCriticalSection(&cancelConnLock
);
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
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.
149 * Handle interrupt signals by canceling the current command, if cancelConn
153 handle_sigint(SIGNAL_ARGS
)
157 CancelRequested
= true;
159 if (cancel_callback
!= NULL
)
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
);
171 write_stderr(cancel_not_sent_msg
);
172 write_stderr(errbuf
);
178 * setup_cancel_handler
180 * Register query cancellation callback for SIGINT.
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
);
195 consoleHandler(DWORD dwCtrlType
)
199 if (dwCtrlType
== CTRL_C_EVENT
||
200 dwCtrlType
== CTRL_BREAK_EVENT
)
202 CancelRequested
= true;
204 if (cancel_callback
!= NULL
)
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
);
217 write_stderr(cancel_not_sent_msg
);
218 write_stderr(errbuf
);
222 LeaveCriticalSection(&cancelConnLock
);
227 /* Return FALSE for any signals not being handled */
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
);