Skip to content

Commit 44e8a96

Browse files
committed
Invent a new, more thread-safe version of PQrequestCancel, called PQcancel.
Use this new function in psql. Implement query cancellation in psql for Windows. Code by Magnus Hagander, documentation and minor editorialization by Tom Lane.
1 parent 80559fa commit 44e8a96

File tree

8 files changed

+399
-118
lines changed

8 files changed

+399
-118
lines changed

doc/src/sgml/libpq.sgml

Lines changed: 135 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<!--
2-
$PostgreSQL: pgsql/doc/src/sgml/libpq.sgml,v 1.166 2004/10/18 22:00:41 tgl Exp $
2+
$PostgreSQL: pgsql/doc/src/sgml/libpq.sgml,v 1.167 2004/10/30 23:09:59 tgl Exp $
33
-->
44

55
<chapter id="libpq">
@@ -2569,49 +2569,11 @@ if <function>PQisBusy</function> returns false (0). It can also call
25692569
A client that uses
25702570
<function>PQsendQuery</function>/<function>PQgetResult</function> can
25712571
also attempt to cancel a command that is still being processed by the
2572-
server.<indexterm><primary>canceling</><secondary>SQL command</></>
2573-
2574-
<variablelist>
2575-
<varlistentry>
2576-
<term><function>PQrequestCancel</function><indexterm><primary>PQrequestCancel</></></term>
2577-
<listitem>
2578-
<para>
2579-
Requests that the server abandon
2580-
processing of the current command.
2581-
<synopsis>
2582-
int PQrequestCancel(PGconn *conn);
2583-
</synopsis>
2584-
</para>
2585-
2586-
<para>
2587-
The return value is 1 if the cancel request was successfully
2588-
dispatched and 0 if not. (If not, <function>PQerrorMessage</function> tells why not.)
2589-
Successful dispatch is no guarantee that the request will have any
2590-
effect, however. Regardless of the return value of <function>PQrequestCancel</function>,
2591-
the application must continue with the normal result-reading
2592-
sequence using <function>PQgetResult</function>. If the cancellation
2593-
is effective, the current command will terminate early and return
2594-
an error result. If the cancellation fails (say, because the
2595-
server was already done processing the command), then there will
2596-
be no visible result at all.
2597-
</para>
2598-
2599-
<para>
2600-
Note that if the current command is part of a transaction block, cancellation
2601-
will abort the whole transaction.
2602-
</para>
2603-
2604-
<para>
2605-
<function>PQrequestCancel</function> can safely be invoked from a signal handler.
2606-
So, it is also possible to use it in conjunction with plain
2607-
<function>PQexec</function>, if the decision to cancel can be made in a signal
2608-
handler. For example, <application>psql</application> invokes
2609-
<function>PQrequestCancel</function> from a <symbol>SIGINT</> signal handler, thus allowing
2610-
interactive cancellation of commands that it issues through <function>PQexec</function>.
2611-
</para>
2612-
</listitem>
2613-
</varlistentry>
2614-
</variablelist>
2572+
server; see <xref linkend="libpq-cancel">. But regardless of the return value
2573+
of <function>PQcancel</function>, the application must continue with the
2574+
normal result-reading sequence using <function>PQgetResult</function>.
2575+
A successful cancellation will simply cause the command to terminate
2576+
sooner than it would have otherwise.
26152577
</para>
26162578

26172579
<para>
@@ -2699,6 +2661,125 @@ and then read the response as described above.
26992661

27002662
</sect1>
27012663

2664+
<sect1 id="libpq-cancel">
2665+
<title>Cancelling Queries in Progress</title>
2666+
2667+
<indexterm zone="libpq-cancel"><primary>canceling</><secondary>SQL command</></>
2668+
2669+
<para>
2670+
A client application can request cancellation of
2671+
a command that is still being processed by the
2672+
server, using the functions described in this section.
2673+
2674+
<variablelist>
2675+
<varlistentry>
2676+
<term><function>PQgetCancel</function><indexterm><primary>PQgetCancel</></></term>
2677+
<listitem>
2678+
<para>
2679+
Creates a data structure containing the information needed to cancel
2680+
a command issued through a particular database connection.
2681+
<synopsis>
2682+
PGcancel *PQgetCancel(PGconn *conn);
2683+
</synopsis>
2684+
</para>
2685+
2686+
<para>
2687+
<function>PQgetCancel</function> creates a
2688+
<structname>PGcancel</><indexterm><primary>PGcancel</></> object given
2689+
a <structname>PGconn</> connection object. It will return NULL if the
2690+
given <parameter>conn</> is NULL or an invalid connection. The
2691+
<structname>PGcancel</> object is an opaque structure that is not meant
2692+
to be accessed directly by the application; it can only be passed to
2693+
<function>PQcancel</function> or <function>PQfreeCancel</function>.
2694+
</para>
2695+
</listitem>
2696+
</varlistentry>
2697+
2698+
<varlistentry>
2699+
<term><function>PQfreeCancel</function><indexterm><primary>PQfreeCancel</></></term>
2700+
<listitem>
2701+
<para>
2702+
Frees a data structure created by <function>PQgetCancel</function>.
2703+
<synopsis>
2704+
void PQfreeCancel(PGcancel *cancel);
2705+
</synopsis>
2706+
</para>
2707+
2708+
<para>
2709+
<function>PQfreeCancel</function> frees a data object previously created
2710+
by <function>PQgetCancel</function>.
2711+
</para>
2712+
</listitem>
2713+
</varlistentry>
2714+
2715+
<varlistentry>
2716+
<term><function>PQcancel</function><indexterm><primary>PQcancel</></></term>
2717+
<listitem>
2718+
<para>
2719+
Requests that the server abandon
2720+
processing of the current command.
2721+
<synopsis>
2722+
int PQcancel(PGcancel *cancel, char *errbuf, int errbufsize);
2723+
</synopsis>
2724+
</para>
2725+
2726+
<para>
2727+
The return value is 1 if the cancel request was successfully
2728+
dispatched and 0 if not. If not, <parameter>errbuf</> is filled with an error
2729+
message explaining why not. <parameter>errbuf</> must be a char array of size
2730+
<parameter>errbufsize</> (the recommended size is 256 bytes).
2731+
</para>
2732+
2733+
<para>
2734+
Successful dispatch is no guarantee that the request will have any effect,
2735+
however. If the cancellation is effective, the current command will terminate
2736+
early and return an error result. If the cancellation fails (say, because the
2737+
server was already done processing the command), then there will be no visible
2738+
result at all.
2739+
</para>
2740+
2741+
<para>
2742+
<function>PQcancel</function> can safely be invoked from a signal handler,
2743+
if the <parameter>errbuf</> is a local variable in the signal handler. The
2744+
<structname>PGcancel</> object is read-only as far as
2745+
<function>PQcancel</function> is concerned, so it can also be invoked from a
2746+
thread that is separate from the one manipulating the <structname>PGconn</>
2747+
object.
2748+
</para>
2749+
</listitem>
2750+
</varlistentry>
2751+
</variablelist>
2752+
2753+
<variablelist>
2754+
<varlistentry>
2755+
<term><function>PQrequestCancel</function><indexterm><primary>PQrequestCancel</></></term>
2756+
<listitem>
2757+
<para>
2758+
Requests that the server abandon
2759+
processing of the current command.
2760+
<synopsis>
2761+
int PQrequestCancel(PGconn *conn);
2762+
</synopsis>
2763+
</para>
2764+
2765+
<para>
2766+
<function>PQrequestCancel</function> is a deprecated variant of
2767+
<function>PQcancel</function>. It operates directly on the
2768+
<structname>PGconn</> object, and in case of failure stores the
2769+
error message in the <structname>PGconn</> object (whence it can be
2770+
retrieved by <function>PQerrorMessage</function>). Although the
2771+
functionality is the same, this approach creates hazards for multiple-thread
2772+
programs and signal handlers, since it is possible that overwriting the
2773+
<structname>PGconn</>'s error message will mess up the operation currently
2774+
in progress on the connection.
2775+
</para>
2776+
</listitem>
2777+
</varlistentry>
2778+
</variablelist>
2779+
</para>
2780+
2781+
</sect1>
2782+
27022783
<sect1 id="libpq-fastpath">
27032784
<title>The Fast-Path Interface</title>
27042785

@@ -3852,11 +3933,16 @@ passed around freely between threads.
38523933
</para>
38533934

38543935
<para>
3855-
The deprecated functions <function>PQoidStatus</function> and
3856-
<function>fe_setauthsvc</function> are not thread-safe and should not be
3857-
used in multithread programs. <function>PQoidStatus</function> can be
3858-
replaced by <function>PQoidValue</function>. There is no good reason to
3859-
call <function>fe_setauthsvc</function> at all.
3936+
The deprecated functions
3937+
<function>PQrequestCancel</function>,
3938+
<function>PQoidStatus</function> and
3939+
<function>fe_setauthsvc</function>
3940+
are not thread-safe and should not be used in multithread programs.
3941+
<function>PQrequestCancel</function> can be replaced by
3942+
<function>PQcancel</function>.
3943+
<function>PQoidStatus</function> can be replaced by
3944+
<function>PQoidValue</function>.
3945+
There is no good reason to call <function>fe_setauthsvc</function> at all.
38603946
</para>
38613947

38623948
<para>

src/bin/psql/common.c

Lines changed: 87 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
*
44
* Copyright (c) 2000-2004, PostgreSQL Global Development Group
55
*
6-
* $PostgreSQL: pgsql/src/bin/psql/common.c,v 1.92 2004/10/10 23:37:40 neilc Exp $
6+
* $PostgreSQL: pgsql/src/bin/psql/common.c,v 1.93 2004/10/30 23:10:50 tgl Exp $
77
*/
88
#include "postgres_fe.h"
99
#include "common.h"
@@ -223,23 +223,33 @@ NoticeProcessor(void *arg, const char *message)
223223
*
224224
* Before we start a query, we enable a SIGINT signal catcher that sends a
225225
* cancel request to the backend. Note that sending the cancel directly from
226-
* the signal handler is safe because PQrequestCancel() is written to make it
227-
* so. We use write() to print to stdout because it's better to use simple
226+
* the signal handler is safe because PQcancel() is written to make it
227+
* so. We use write() to print to stderr because it's better to use simple
228228
* facilities in a signal handler.
229+
*
230+
* On win32, the signal cancelling happens on a separate thread, because
231+
* that's how SetConsoleCtrlHandler works. The PQcancel function is safe
232+
* for this (unlike PQrequestCancel). However, a CRITICAL_SECTION is required
233+
* to protect the PGcancel structure against being changed while the other
234+
* thread is using it.
229235
*/
230-
static PGconn *volatile cancelConn = NULL;
236+
static PGcancel *cancelConn = NULL;
237+
#ifdef WIN32
238+
static CRITICAL_SECTION cancelConnLock;
239+
#endif
231240

232241
volatile bool cancel_pressed = false;
233242

243+
#define write_stderr(str) write(fileno(stderr), str, strlen(str))
234244

235-
#ifndef WIN32
236245

237-
#define write_stderr(String) write(fileno(stderr), String, strlen(String))
246+
#ifndef WIN32
238247

239248
void
240249
handle_sigint(SIGNAL_ARGS)
241250
{
242251
int save_errno = errno;
252+
char errbuf[256];
243253

244254
/* Don't muck around if prompting for a password. */
245255
if (prompt_state)
@@ -250,17 +260,60 @@ handle_sigint(SIGNAL_ARGS)
250260

251261
cancel_pressed = true;
252262

253-
if (PQrequestCancel(cancelConn))
263+
if (PQcancel(cancelConn, errbuf, sizeof(errbuf)))
254264
write_stderr("Cancel request sent\n");
255265
else
256266
{
257267
write_stderr("Could not send cancel request: ");
258-
write_stderr(PQerrorMessage(cancelConn));
268+
write_stderr(errbuf);
259269
}
260270
errno = save_errno; /* just in case the write changed it */
261271
}
262-
#endif /* not WIN32 */
263272

273+
#else /* WIN32 */
274+
275+
static BOOL WINAPI
276+
consoleHandler(DWORD dwCtrlType)
277+
{
278+
char errbuf[256];
279+
280+
if (dwCtrlType == CTRL_C_EVENT ||
281+
dwCtrlType == CTRL_BREAK_EVENT)
282+
{
283+
if (prompt_state)
284+
return TRUE;
285+
286+
/* Perform query cancel */
287+
EnterCriticalSection(&cancelConnLock);
288+
if (cancelConn != NULL)
289+
{
290+
cancel_pressed = true;
291+
292+
if (PQcancel(cancelConn, errbuf, sizeof(errbuf)))
293+
write_stderr("Cancel request sent\n");
294+
else
295+
{
296+
write_stderr("Could not send cancel request: ");
297+
write_stderr(errbuf);
298+
}
299+
}
300+
LeaveCriticalSection(&cancelConnLock);
301+
302+
return TRUE;
303+
}
304+
else
305+
/* Return FALSE for any signals not being handled */
306+
return FALSE;
307+
}
308+
309+
void
310+
setup_cancel_handler(void)
311+
{
312+
InitializeCriticalSection(&cancelConnLock);
313+
SetConsoleCtrlHandler(consoleHandler, TRUE);
314+
}
315+
316+
#endif /* WIN32 */
264317

265318

266319
/* ConnectionUp
@@ -327,20 +380,42 @@ CheckConnection(void)
327380
static void
328381
SetCancelConn(void)
329382
{
330-
cancelConn = pset.db;
383+
#ifdef WIN32
384+
EnterCriticalSection(&cancelConnLock);
385+
#endif
386+
387+
/* Free the old one if we have one */
388+
if (cancelConn != NULL)
389+
PQfreeCancel(cancelConn);
390+
391+
cancelConn = PQgetCancel(pset.db);
392+
393+
#ifdef WIN32
394+
LeaveCriticalSection(&cancelConnLock);
395+
#endif
331396
}
332397

333398

334399
/*
335400
* ResetCancelConn
336401
*
337-
* Set cancelConn to NULL. I don't know what this means exactly, but it saves
338-
* having to export the variable.
402+
* Free the current cancel connection, if any, and set to NULL.
339403
*/
340404
void
341405
ResetCancelConn(void)
342406
{
407+
#ifdef WIN32
408+
EnterCriticalSection(&cancelConnLock);
409+
#endif
410+
411+
if (cancelConn)
412+
PQfreeCancel(cancelConn);
413+
343414
cancelConn = NULL;
415+
416+
#ifdef WIN32
417+
LeaveCriticalSection(&cancelConnLock);
418+
#endif
344419
}
345420

346421

src/bin/psql/common.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
*
44
* Copyright (c) 2000-2004, PostgreSQL Global Development Group
55
*
6-
* $PostgreSQL: pgsql/src/bin/psql/common.h,v 1.39 2004/08/29 04:13:02 momjian Exp $
6+
* $PostgreSQL: pgsql/src/bin/psql/common.h,v 1.40 2004/10/30 23:10:50 tgl Exp $
77
*/
88
#ifndef COMMON_H
99
#define COMMON_H
@@ -48,7 +48,9 @@ extern void ResetCancelConn(void);
4848

4949
#ifndef WIN32
5050
extern void handle_sigint(SIGNAL_ARGS);
51-
#endif /* not WIN32 */
51+
#else
52+
extern void setup_cancel_handler(void);
53+
#endif
5254

5355
extern PGresult *PSQLexec(const char *query, bool start_xact);
5456

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy