mirror of
https://git.postgresql.org/git/postgresql.git
synced 2025-02-05 19:09:58 +08:00
Refactor query cancellation code into src/fe_utils/
Originally, this code was duplicated in src/bin/psql/ and src/bin/scripts/, but it can be useful for other frontend applications, like pgbench. This refactoring offers the possibility to setup a custom callback which would get called in the signal handler for SIGINT or when the interruption console events happen on Windows. Author: Fabien Coelho, with contributions from Michael Paquier Reviewed-by: Álvaro Herrera, Ibrar Ahmed Discussion: https://postgr.es/m/alpine.DEB.2.21.1910311939430.27369@lancre
This commit is contained in:
parent
c01ac6dcba
commit
a4fd3aa719
@ -29,6 +29,7 @@
|
|||||||
#include "copy.h"
|
#include "copy.h"
|
||||||
#include "crosstabview.h"
|
#include "crosstabview.h"
|
||||||
#include "describe.h"
|
#include "describe.h"
|
||||||
|
#include "fe_utils/cancel.h"
|
||||||
#include "fe_utils/print.h"
|
#include "fe_utils/print.h"
|
||||||
#include "fe_utils/string_utils.h"
|
#include "fe_utils/string_utils.h"
|
||||||
#include "help.h"
|
#include "help.h"
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
#include "common/logging.h"
|
#include "common/logging.h"
|
||||||
#include "copy.h"
|
#include "copy.h"
|
||||||
#include "crosstabview.h"
|
#include "crosstabview.h"
|
||||||
|
#include "fe_utils/cancel.h"
|
||||||
#include "fe_utils/mbprint.h"
|
#include "fe_utils/mbprint.h"
|
||||||
#include "fe_utils/string_utils.h"
|
#include "fe_utils/string_utils.h"
|
||||||
#include "portability/instr_time.h"
|
#include "portability/instr_time.h"
|
||||||
@ -228,58 +229,28 @@ NoticeProcessor(void *arg, const char *message)
|
|||||||
* Code to support query cancellation
|
* Code to support query cancellation
|
||||||
*
|
*
|
||||||
* Before we start a query, we enable the SIGINT signal catcher to send a
|
* Before we start a query, we enable the SIGINT signal catcher to send a
|
||||||
* cancel request to the backend. Note that sending the cancel directly from
|
* cancel request to the backend.
|
||||||
* the signal handler is safe because PQcancel() is written to make it
|
|
||||||
* so. We use write() to report to stderr because it's better to use simple
|
|
||||||
* facilities in a signal handler.
|
|
||||||
*
|
|
||||||
* On win32, the signal canceling happens on a separate thread, because
|
|
||||||
* that's how SetConsoleCtrlHandler works. The PQcancel function is safe
|
|
||||||
* for this (unlike PQrequestCancel). However, a CRITICAL_SECTION is required
|
|
||||||
* to protect the PGcancel structure against being changed while the signal
|
|
||||||
* thread is using it.
|
|
||||||
*
|
*
|
||||||
* SIGINT is supposed to abort all long-running psql operations, not only
|
* SIGINT is supposed to abort all long-running psql operations, not only
|
||||||
* database queries. In most places, this is accomplished by checking
|
* database queries. In most places, this is accomplished by checking
|
||||||
* cancel_pressed during long-running loops. However, that won't work when
|
* CancelRequested during long-running loops. However, that won't work when
|
||||||
* blocked on user input (in readline() or fgets()). In those places, we
|
* blocked on user input (in readline() or fgets()). In those places, we
|
||||||
* set sigint_interrupt_enabled true while blocked, instructing the signal
|
* set sigint_interrupt_enabled true while blocked, instructing the signal
|
||||||
* catcher to longjmp through sigint_interrupt_jmp. We assume readline and
|
* catcher to longjmp through sigint_interrupt_jmp. We assume readline and
|
||||||
* fgets are coded to handle possible interruption. (XXX currently this does
|
* fgets are coded to handle possible interruption.
|
||||||
* not work on win32, so control-C is less useful there)
|
*
|
||||||
|
* On Windows, currently this does not work, so control-C is less useful
|
||||||
|
* there, and the callback is just a no-op.
|
||||||
*/
|
*/
|
||||||
volatile bool sigint_interrupt_enabled = false;
|
volatile bool sigint_interrupt_enabled = false;
|
||||||
|
|
||||||
sigjmp_buf sigint_interrupt_jmp;
|
sigjmp_buf sigint_interrupt_jmp;
|
||||||
|
|
||||||
static PGcancel *volatile cancelConn = NULL;
|
|
||||||
|
|
||||||
#ifdef WIN32
|
|
||||||
static CRITICAL_SECTION cancelConnLock;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Write a simple string to stderr --- must be safe in a signal handler.
|
|
||||||
* We ignore the write() result since there's not much we could do about it.
|
|
||||||
* Certain compilers make that harder than it ought to be.
|
|
||||||
*/
|
|
||||||
#define write_stderr(str) \
|
|
||||||
do { \
|
|
||||||
const char *str_ = (str); \
|
|
||||||
int rc_; \
|
|
||||||
rc_ = write(fileno(stderr), str_, strlen(str_)); \
|
|
||||||
(void) rc_; \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef WIN32
|
#ifndef WIN32
|
||||||
|
|
||||||
static void
|
static void
|
||||||
handle_sigint(SIGNAL_ARGS)
|
psql_cancel_callback(void)
|
||||||
{
|
{
|
||||||
int save_errno = errno;
|
|
||||||
char errbuf[256];
|
|
||||||
|
|
||||||
/* if we are waiting for input, longjmp out of it */
|
/* if we are waiting for input, longjmp out of it */
|
||||||
if (sigint_interrupt_enabled)
|
if (sigint_interrupt_enabled)
|
||||||
{
|
{
|
||||||
@ -288,74 +259,24 @@ handle_sigint(SIGNAL_ARGS)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* else, set cancel flag to stop any long-running loops */
|
/* else, set cancel flag to stop any long-running loops */
|
||||||
cancel_pressed = true;
|
CancelRequested = true;
|
||||||
|
|
||||||
/* and send QueryCancel if we are processing a database query */
|
|
||||||
if (cancelConn != NULL)
|
|
||||||
{
|
|
||||||
if (PQcancel(cancelConn, errbuf, sizeof(errbuf)))
|
|
||||||
write_stderr("Cancel request sent\n");
|
|
||||||
else
|
|
||||||
{
|
|
||||||
write_stderr("Could not send cancel request: ");
|
|
||||||
write_stderr(errbuf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
errno = save_errno; /* just in case the write changed it */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
static void
|
||||||
|
psql_cancel_callback(void)
|
||||||
|
{
|
||||||
|
/* nothing to do here */
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* !WIN32 */
|
||||||
|
|
||||||
void
|
void
|
||||||
setup_cancel_handler(void)
|
psql_setup_cancel_handler(void)
|
||||||
{
|
{
|
||||||
pqsignal(SIGINT, handle_sigint);
|
setup_cancel_handler(psql_cancel_callback);
|
||||||
}
|
}
|
||||||
#else /* WIN32 */
|
|
||||||
|
|
||||||
static BOOL WINAPI
|
|
||||||
consoleHandler(DWORD dwCtrlType)
|
|
||||||
{
|
|
||||||
char errbuf[256];
|
|
||||||
|
|
||||||
if (dwCtrlType == CTRL_C_EVENT ||
|
|
||||||
dwCtrlType == CTRL_BREAK_EVENT)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* Can't longjmp here, because we are in wrong thread :-(
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* set cancel flag to stop any long-running loops */
|
|
||||||
cancel_pressed = true;
|
|
||||||
|
|
||||||
/* and send QueryCancel if we are processing a database query */
|
|
||||||
EnterCriticalSection(&cancelConnLock);
|
|
||||||
if (cancelConn != NULL)
|
|
||||||
{
|
|
||||||
if (PQcancel(cancelConn, errbuf, sizeof(errbuf)))
|
|
||||||
write_stderr("Cancel request sent\n");
|
|
||||||
else
|
|
||||||
{
|
|
||||||
write_stderr("Could not send cancel request: ");
|
|
||||||
write_stderr(errbuf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LeaveCriticalSection(&cancelConnLock);
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
/* Return FALSE for any signals not being handled */
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
setup_cancel_handler(void)
|
|
||||||
{
|
|
||||||
InitializeCriticalSection(&cancelConnLock);
|
|
||||||
|
|
||||||
SetConsoleCtrlHandler(consoleHandler, TRUE);
|
|
||||||
}
|
|
||||||
#endif /* WIN32 */
|
|
||||||
|
|
||||||
|
|
||||||
/* ConnectionUp
|
/* ConnectionUp
|
||||||
@ -428,62 +349,6 @@ CheckConnection(void)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* SetCancelConn
|
|
||||||
*
|
|
||||||
* Set cancelConn to point to the current database connection.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
SetCancelConn(void)
|
|
||||||
{
|
|
||||||
PGcancel *oldCancelConn;
|
|
||||||
|
|
||||||
#ifdef WIN32
|
|
||||||
EnterCriticalSection(&cancelConnLock);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Free the old one if we have one */
|
|
||||||
oldCancelConn = cancelConn;
|
|
||||||
/* be sure handle_sigint doesn't use pointer while freeing */
|
|
||||||
cancelConn = NULL;
|
|
||||||
|
|
||||||
if (oldCancelConn != NULL)
|
|
||||||
PQfreeCancel(oldCancelConn);
|
|
||||||
|
|
||||||
cancelConn = PQgetCancel(pset.db);
|
|
||||||
|
|
||||||
#ifdef WIN32
|
|
||||||
LeaveCriticalSection(&cancelConnLock);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* ResetCancelConn
|
|
||||||
*
|
|
||||||
* Free the current cancel connection, if any, and set to NULL.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
ResetCancelConn(void)
|
|
||||||
{
|
|
||||||
PGcancel *oldCancelConn;
|
|
||||||
|
|
||||||
#ifdef WIN32
|
|
||||||
EnterCriticalSection(&cancelConnLock);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
oldCancelConn = cancelConn;
|
|
||||||
/* be sure handle_sigint doesn't use pointer while freeing */
|
|
||||||
cancelConn = NULL;
|
|
||||||
|
|
||||||
if (oldCancelConn != NULL)
|
|
||||||
PQfreeCancel(oldCancelConn);
|
|
||||||
|
|
||||||
#ifdef WIN32
|
|
||||||
LeaveCriticalSection(&cancelConnLock);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* AcceptResult
|
* AcceptResult
|
||||||
@ -707,7 +572,7 @@ PSQLexec(const char *query)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
SetCancelConn();
|
SetCancelConn(pset.db);
|
||||||
|
|
||||||
res = PQexec(pset.db, query);
|
res = PQexec(pset.db, query);
|
||||||
|
|
||||||
@ -746,7 +611,7 @@ PSQLexecWatch(const char *query, const printQueryOpt *opt)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
SetCancelConn();
|
SetCancelConn(pset.db);
|
||||||
|
|
||||||
if (pset.timing)
|
if (pset.timing)
|
||||||
INSTR_TIME_SET_CURRENT(before);
|
INSTR_TIME_SET_CURRENT(before);
|
||||||
@ -773,7 +638,7 @@ PSQLexecWatch(const char *query, const printQueryOpt *opt)
|
|||||||
* consumed. The user's intention, though, is to cancel the entire watch
|
* consumed. The user's intention, though, is to cancel the entire watch
|
||||||
* process, so detect a sent cancellation request and exit in this case.
|
* process, so detect a sent cancellation request and exit in this case.
|
||||||
*/
|
*/
|
||||||
if (cancel_pressed)
|
if (CancelRequested)
|
||||||
{
|
{
|
||||||
PQclear(res);
|
PQclear(res);
|
||||||
return 0;
|
return 0;
|
||||||
@ -973,8 +838,8 @@ ExecQueryTuples(const PGresult *result)
|
|||||||
{
|
{
|
||||||
const char *query = PQgetvalue(result, r, c);
|
const char *query = PQgetvalue(result, r, c);
|
||||||
|
|
||||||
/* Abandon execution if cancel_pressed */
|
/* Abandon execution if CancelRequested */
|
||||||
if (cancel_pressed)
|
if (CancelRequested)
|
||||||
goto loop_exit;
|
goto loop_exit;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1091,7 +956,7 @@ ProcessResult(PGresult **results)
|
|||||||
FILE *copystream;
|
FILE *copystream;
|
||||||
PGresult *copy_result;
|
PGresult *copy_result;
|
||||||
|
|
||||||
SetCancelConn();
|
SetCancelConn(pset.db);
|
||||||
if (result_status == PGRES_COPY_OUT)
|
if (result_status == PGRES_COPY_OUT)
|
||||||
{
|
{
|
||||||
bool need_close = false;
|
bool need_close = false;
|
||||||
@ -1342,7 +1207,7 @@ SendQuery(const char *query)
|
|||||||
if (fgets(buf, sizeof(buf), stdin) != NULL)
|
if (fgets(buf, sizeof(buf), stdin) != NULL)
|
||||||
if (buf[0] == 'x')
|
if (buf[0] == 'x')
|
||||||
goto sendquery_cleanup;
|
goto sendquery_cleanup;
|
||||||
if (cancel_pressed)
|
if (CancelRequested)
|
||||||
goto sendquery_cleanup;
|
goto sendquery_cleanup;
|
||||||
}
|
}
|
||||||
else if (pset.echo == PSQL_ECHO_QUERIES)
|
else if (pset.echo == PSQL_ECHO_QUERIES)
|
||||||
@ -1360,7 +1225,7 @@ SendQuery(const char *query)
|
|||||||
fflush(pset.logfile);
|
fflush(pset.logfile);
|
||||||
}
|
}
|
||||||
|
|
||||||
SetCancelConn();
|
SetCancelConn(pset.db);
|
||||||
|
|
||||||
transaction_status = PQtransactionStatus(pset.db);
|
transaction_status = PQtransactionStatus(pset.db);
|
||||||
|
|
||||||
@ -1886,7 +1751,7 @@ ExecQueryUsingCursor(const char *query, double *elapsed_msec)
|
|||||||
* writing things to the stream, we presume $PAGER has disappeared and
|
* writing things to the stream, we presume $PAGER has disappeared and
|
||||||
* stop bothering to pull down more data.
|
* stop bothering to pull down more data.
|
||||||
*/
|
*/
|
||||||
if (ntuples < fetch_count || cancel_pressed || flush_error ||
|
if (ntuples < fetch_count || CancelRequested || flush_error ||
|
||||||
ferror(fout))
|
ferror(fout))
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -26,10 +26,7 @@ extern volatile bool sigint_interrupt_enabled;
|
|||||||
|
|
||||||
extern sigjmp_buf sigint_interrupt_jmp;
|
extern sigjmp_buf sigint_interrupt_jmp;
|
||||||
|
|
||||||
extern void setup_cancel_handler(void);
|
extern void psql_setup_cancel_handler(void);
|
||||||
|
|
||||||
extern void SetCancelConn(void);
|
|
||||||
extern void ResetCancelConn(void);
|
|
||||||
|
|
||||||
extern PGresult *PSQLexec(const char *query);
|
extern PGresult *PSQLexec(const char *query);
|
||||||
extern int PSQLexecWatch(const char *query, const printQueryOpt *opt);
|
extern int PSQLexecWatch(const char *query, const printQueryOpt *opt);
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "common/logging.h"
|
#include "common/logging.h"
|
||||||
|
#include "fe_utils/cancel.h"
|
||||||
#include "large_obj.h"
|
#include "large_obj.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
|
|
||||||
@ -146,7 +147,7 @@ do_lo_export(const char *loid_arg, const char *filename_arg)
|
|||||||
if (!start_lo_xact("\\lo_export", &own_transaction))
|
if (!start_lo_xact("\\lo_export", &own_transaction))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
SetCancelConn();
|
SetCancelConn(NULL);
|
||||||
status = lo_export(pset.db, atooid(loid_arg), filename_arg);
|
status = lo_export(pset.db, atooid(loid_arg), filename_arg);
|
||||||
ResetCancelConn();
|
ResetCancelConn();
|
||||||
|
|
||||||
@ -182,7 +183,7 @@ do_lo_import(const char *filename_arg, const char *comment_arg)
|
|||||||
if (!start_lo_xact("\\lo_import", &own_transaction))
|
if (!start_lo_xact("\\lo_import", &own_transaction))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
SetCancelConn();
|
SetCancelConn(NULL);
|
||||||
loid = lo_import(pset.db, filename_arg);
|
loid = lo_import(pset.db, filename_arg);
|
||||||
ResetCancelConn();
|
ResetCancelConn();
|
||||||
|
|
||||||
@ -244,7 +245,7 @@ do_lo_unlink(const char *loid_arg)
|
|||||||
if (!start_lo_xact("\\lo_unlink", &own_transaction))
|
if (!start_lo_xact("\\lo_unlink", &own_transaction))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
SetCancelConn();
|
SetCancelConn(NULL);
|
||||||
status = lo_unlink(pset.db, loid);
|
status = lo_unlink(pset.db, loid);
|
||||||
ResetCancelConn();
|
ResetCancelConn();
|
||||||
|
|
||||||
|
@ -301,7 +301,7 @@ main(int argc, char *argv[])
|
|||||||
exit(EXIT_BADCONN);
|
exit(EXIT_BADCONN);
|
||||||
}
|
}
|
||||||
|
|
||||||
setup_cancel_handler();
|
psql_setup_cancel_handler();
|
||||||
|
|
||||||
PQsetNoticeProcessor(pset.db, NoticeProcessor, NULL);
|
PQsetNoticeProcessor(pset.db, NoticeProcessor, NULL);
|
||||||
|
|
||||||
|
@ -133,7 +133,7 @@ main(int argc, char *argv[])
|
|||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
setup_cancel_handler();
|
setup_cancel_handler(NULL);
|
||||||
|
|
||||||
if (alldb)
|
if (alldb)
|
||||||
{
|
{
|
||||||
|
@ -24,14 +24,6 @@
|
|||||||
|
|
||||||
#define ERRCODE_UNDEFINED_TABLE "42P01"
|
#define ERRCODE_UNDEFINED_TABLE "42P01"
|
||||||
|
|
||||||
|
|
||||||
static PGcancel *volatile cancelConn = NULL;
|
|
||||||
bool CancelRequested = false;
|
|
||||||
|
|
||||||
#ifdef WIN32
|
|
||||||
static CRITICAL_SECTION cancelConnLock;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Provide strictly harmonized handling of --help and --version
|
* Provide strictly harmonized handling of --help and --version
|
||||||
* options.
|
* options.
|
||||||
@ -465,142 +457,3 @@ yesno_prompt(const char *question)
|
|||||||
_(PG_YESLETTER), _(PG_NOLETTER));
|
_(PG_YESLETTER), _(PG_NOLETTER));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* SetCancelConn
|
|
||||||
*
|
|
||||||
* Set cancelConn to point to the current database connection.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
SetCancelConn(PGconn *conn)
|
|
||||||
{
|
|
||||||
PGcancel *oldCancelConn;
|
|
||||||
|
|
||||||
#ifdef WIN32
|
|
||||||
EnterCriticalSection(&cancelConnLock);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Free the old one if we have one */
|
|
||||||
oldCancelConn = cancelConn;
|
|
||||||
|
|
||||||
/* be sure handle_sigint doesn't use pointer while freeing */
|
|
||||||
cancelConn = NULL;
|
|
||||||
|
|
||||||
if (oldCancelConn != NULL)
|
|
||||||
PQfreeCancel(oldCancelConn);
|
|
||||||
|
|
||||||
cancelConn = PQgetCancel(conn);
|
|
||||||
|
|
||||||
#ifdef WIN32
|
|
||||||
LeaveCriticalSection(&cancelConnLock);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* ResetCancelConn
|
|
||||||
*
|
|
||||||
* Free the current cancel connection, if any, and set to NULL.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
ResetCancelConn(void)
|
|
||||||
{
|
|
||||||
PGcancel *oldCancelConn;
|
|
||||||
|
|
||||||
#ifdef WIN32
|
|
||||||
EnterCriticalSection(&cancelConnLock);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
oldCancelConn = cancelConn;
|
|
||||||
|
|
||||||
/* be sure handle_sigint doesn't use pointer while freeing */
|
|
||||||
cancelConn = NULL;
|
|
||||||
|
|
||||||
if (oldCancelConn != NULL)
|
|
||||||
PQfreeCancel(oldCancelConn);
|
|
||||||
|
|
||||||
#ifdef WIN32
|
|
||||||
LeaveCriticalSection(&cancelConnLock);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef WIN32
|
|
||||||
/*
|
|
||||||
* Handle interrupt signals by canceling the current command, if a cancelConn
|
|
||||||
* is set.
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
handle_sigint(SIGNAL_ARGS)
|
|
||||||
{
|
|
||||||
int save_errno = errno;
|
|
||||||
char errbuf[256];
|
|
||||||
|
|
||||||
/* Send QueryCancel if we are processing a database query */
|
|
||||||
if (cancelConn != NULL)
|
|
||||||
{
|
|
||||||
if (PQcancel(cancelConn, errbuf, sizeof(errbuf)))
|
|
||||||
{
|
|
||||||
CancelRequested = true;
|
|
||||||
fprintf(stderr, _("Cancel request sent\n"));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
fprintf(stderr, _("Could not send cancel request: %s"), errbuf);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
CancelRequested = true;
|
|
||||||
|
|
||||||
errno = save_errno; /* just in case the write changed it */
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
setup_cancel_handler(void)
|
|
||||||
{
|
|
||||||
pqsignal(SIGINT, handle_sigint);
|
|
||||||
}
|
|
||||||
#else /* WIN32 */
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Console control handler for Win32. Note that the control handler will
|
|
||||||
* execute on a *different thread* than the main one, so we need to do
|
|
||||||
* proper locking around those structures.
|
|
||||||
*/
|
|
||||||
static BOOL WINAPI
|
|
||||||
consoleHandler(DWORD dwCtrlType)
|
|
||||||
{
|
|
||||||
char errbuf[256];
|
|
||||||
|
|
||||||
if (dwCtrlType == CTRL_C_EVENT ||
|
|
||||||
dwCtrlType == CTRL_BREAK_EVENT)
|
|
||||||
{
|
|
||||||
/* Send QueryCancel if we are processing a database query */
|
|
||||||
EnterCriticalSection(&cancelConnLock);
|
|
||||||
if (cancelConn != NULL)
|
|
||||||
{
|
|
||||||
if (PQcancel(cancelConn, errbuf, sizeof(errbuf)))
|
|
||||||
{
|
|
||||||
fprintf(stderr, _("Cancel request sent\n"));
|
|
||||||
CancelRequested = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
fprintf(stderr, _("Could not send cancel request: %s"), errbuf);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
CancelRequested = true;
|
|
||||||
|
|
||||||
LeaveCriticalSection(&cancelConnLock);
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
/* Return FALSE for any signals not being handled */
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
setup_cancel_handler(void)
|
|
||||||
{
|
|
||||||
InitializeCriticalSection(&cancelConnLock);
|
|
||||||
|
|
||||||
SetConsoleCtrlHandler(consoleHandler, TRUE);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* WIN32 */
|
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
#define COMMON_H
|
#define COMMON_H
|
||||||
|
|
||||||
#include "common/username.h"
|
#include "common/username.h"
|
||||||
|
#include "fe_utils/cancel.h"
|
||||||
#include "getopt_long.h" /* pgrminclude ignore */
|
#include "getopt_long.h" /* pgrminclude ignore */
|
||||||
#include "libpq-fe.h"
|
#include "libpq-fe.h"
|
||||||
#include "pqexpbuffer.h" /* pgrminclude ignore */
|
#include "pqexpbuffer.h" /* pgrminclude ignore */
|
||||||
@ -60,10 +61,4 @@ extern void appendQualifiedRelation(PQExpBuffer buf, const char *name,
|
|||||||
|
|
||||||
extern bool yesno_prompt(const char *question);
|
extern bool yesno_prompt(const char *question);
|
||||||
|
|
||||||
extern void setup_cancel_handler(void);
|
|
||||||
|
|
||||||
extern void SetCancelConn(PGconn *conn);
|
|
||||||
extern void ResetCancelConn(void);
|
|
||||||
|
|
||||||
|
|
||||||
#endif /* COMMON_H */
|
#endif /* COMMON_H */
|
||||||
|
@ -187,7 +187,7 @@ main(int argc, char *argv[])
|
|||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
setup_cancel_handler();
|
setup_cancel_handler(NULL);
|
||||||
|
|
||||||
if (alldb)
|
if (alldb)
|
||||||
{
|
{
|
||||||
|
@ -257,7 +257,7 @@ main(int argc, char *argv[])
|
|||||||
/* allow 'and_analyze' with 'analyze_only' */
|
/* allow 'and_analyze' with 'analyze_only' */
|
||||||
}
|
}
|
||||||
|
|
||||||
setup_cancel_handler();
|
setup_cancel_handler(NULL);
|
||||||
|
|
||||||
/* Avoid opening extra connections. */
|
/* Avoid opening extra connections. */
|
||||||
if (tbl_count && (concurrentCons > tbl_count))
|
if (tbl_count && (concurrentCons > tbl_count))
|
||||||
|
@ -20,6 +20,7 @@ include $(top_builddir)/src/Makefile.global
|
|||||||
override CPPFLAGS := -DFRONTEND -I$(libpq_srcdir) $(CPPFLAGS)
|
override CPPFLAGS := -DFRONTEND -I$(libpq_srcdir) $(CPPFLAGS)
|
||||||
|
|
||||||
OBJS = \
|
OBJS = \
|
||||||
|
cancel.o \
|
||||||
conditional.o \
|
conditional.o \
|
||||||
mbprint.o \
|
mbprint.o \
|
||||||
print.o \
|
print.o \
|
||||||
|
225
src/fe_utils/cancel.c
Normal file
225
src/fe_utils/cancel.c
Normal file
@ -0,0 +1,225 @@
|
|||||||
|
/*------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* Query cancellation support for frontend code
|
||||||
|
*
|
||||||
|
* Assorted utility functions to control query cancellation with signal
|
||||||
|
* handler for SIGINT.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
|
||||||
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
|
*
|
||||||
|
* src/fe-utils/cancel.c
|
||||||
|
*
|
||||||
|
*------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "postgres_fe.h"
|
||||||
|
|
||||||
|
#include <signal.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "fe_utils/cancel.h"
|
||||||
|
#include "fe_utils/connect.h"
|
||||||
|
#include "fe_utils/string_utils.h"
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Write a simple string to stderr --- must be safe in a signal handler.
|
||||||
|
* We ignore the write() result since there's not much we could do about it.
|
||||||
|
* Certain compilers make that harder than it ought to be.
|
||||||
|
*/
|
||||||
|
#define write_stderr(str) \
|
||||||
|
do { \
|
||||||
|
const char *str_ = (str); \
|
||||||
|
int rc_; \
|
||||||
|
rc_ = write(fileno(stderr), str_, strlen(str_)); \
|
||||||
|
(void) rc_; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
static PGcancel *volatile cancelConn = NULL;
|
||||||
|
bool CancelRequested = false;
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
static CRITICAL_SECTION cancelConnLock;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Additional callback for cancellations.
|
||||||
|
*/
|
||||||
|
static void (*cancel_callback) (void) = NULL;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SetCancelConn
|
||||||
|
*
|
||||||
|
* Set cancelConn to point to the current database connection.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
SetCancelConn(PGconn *conn)
|
||||||
|
{
|
||||||
|
PGcancel *oldCancelConn;
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
EnterCriticalSection(&cancelConnLock);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Free the old one if we have one */
|
||||||
|
oldCancelConn = cancelConn;
|
||||||
|
|
||||||
|
/* be sure handle_sigint doesn't use pointer while freeing */
|
||||||
|
cancelConn = NULL;
|
||||||
|
|
||||||
|
if (oldCancelConn != NULL)
|
||||||
|
PQfreeCancel(oldCancelConn);
|
||||||
|
|
||||||
|
cancelConn = PQgetCancel(conn);
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
LeaveCriticalSection(&cancelConnLock);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ResetCancelConn
|
||||||
|
*
|
||||||
|
* Free the current cancel connection, if any, and set to NULL.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
ResetCancelConn(void)
|
||||||
|
{
|
||||||
|
PGcancel *oldCancelConn;
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
EnterCriticalSection(&cancelConnLock);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
oldCancelConn = cancelConn;
|
||||||
|
|
||||||
|
/* be sure handle_sigint doesn't use pointer while freeing */
|
||||||
|
cancelConn = NULL;
|
||||||
|
|
||||||
|
if (oldCancelConn != NULL)
|
||||||
|
PQfreeCancel(oldCancelConn);
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
LeaveCriticalSection(&cancelConnLock);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Code to support query cancellation
|
||||||
|
*
|
||||||
|
* Note that sending the cancel directly from the signal handler is safe
|
||||||
|
* because PQcancel() is written to make it so. We use write() to report
|
||||||
|
* to stderr because it's better to use simple facilities in a signal
|
||||||
|
* handler.
|
||||||
|
*
|
||||||
|
* On Windows, the signal canceling happens on a separate thread, because
|
||||||
|
* that's how SetConsoleCtrlHandler works. The PQcancel function is safe
|
||||||
|
* for this (unlike PQrequestCancel). However, a CRITICAL_SECTION is required
|
||||||
|
* to protect the PGcancel structure against being changed while the signal
|
||||||
|
* thread is using it.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef WIN32
|
||||||
|
|
||||||
|
/*
|
||||||
|
* handle_sigint
|
||||||
|
*
|
||||||
|
* Handle interrupt signals by canceling the current command, if cancelConn
|
||||||
|
* is set.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
handle_sigint(SIGNAL_ARGS)
|
||||||
|
{
|
||||||
|
int save_errno = errno;
|
||||||
|
char errbuf[256];
|
||||||
|
|
||||||
|
if (cancel_callback != NULL)
|
||||||
|
cancel_callback();
|
||||||
|
|
||||||
|
/* Send QueryCancel if we are processing a database query */
|
||||||
|
if (cancelConn != NULL)
|
||||||
|
{
|
||||||
|
if (PQcancel(cancelConn, errbuf, sizeof(errbuf)))
|
||||||
|
{
|
||||||
|
CancelRequested = true;
|
||||||
|
write_stderr(_("Cancel request sent\n"));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
write_stderr(_("Could not send cancel request: "));
|
||||||
|
write_stderr(errbuf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
CancelRequested = true;
|
||||||
|
|
||||||
|
errno = save_errno; /* just in case the write changed it */
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* setup_cancel_handler
|
||||||
|
*
|
||||||
|
* Register query cancellation callback for SIGINT.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
setup_cancel_handler(void (*callback) (void))
|
||||||
|
{
|
||||||
|
cancel_callback = callback;
|
||||||
|
pqsignal(SIGINT, handle_sigint);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else /* WIN32 */
|
||||||
|
|
||||||
|
static BOOL WINAPI
|
||||||
|
consoleHandler(DWORD dwCtrlType)
|
||||||
|
{
|
||||||
|
char errbuf[256];
|
||||||
|
|
||||||
|
if (dwCtrlType == CTRL_C_EVENT ||
|
||||||
|
dwCtrlType == CTRL_BREAK_EVENT)
|
||||||
|
{
|
||||||
|
if (cancel_callback != NULL)
|
||||||
|
cancel_callback();
|
||||||
|
|
||||||
|
/* Send QueryCancel if we are processing a database query */
|
||||||
|
EnterCriticalSection(&cancelConnLock);
|
||||||
|
if (cancelConn != NULL)
|
||||||
|
{
|
||||||
|
if (PQcancel(cancelConn, errbuf, sizeof(errbuf)))
|
||||||
|
{
|
||||||
|
write_stderr(_("Cancel request sent\n"));
|
||||||
|
CancelRequested = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
write_stderr(_("Could not send cancel request: %s"));
|
||||||
|
write_stderr(errbuf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
CancelRequested = true;
|
||||||
|
|
||||||
|
LeaveCriticalSection(&cancelConnLock);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
/* Return FALSE for any signals not being handled */
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
setup_cancel_handler(void (*callback) (void))
|
||||||
|
{
|
||||||
|
cancel_callback = callback;
|
||||||
|
|
||||||
|
InitializeCriticalSection(&cancelConnLock);
|
||||||
|
|
||||||
|
SetConsoleCtrlHandler(consoleHandler, TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* WIN32 */
|
30
src/include/fe_utils/cancel.h
Normal file
30
src/include/fe_utils/cancel.h
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
/*-------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* Query cancellation support for frontend code
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
|
||||||
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
|
*
|
||||||
|
* src/include/fe_utils/cancel.h
|
||||||
|
*
|
||||||
|
*-------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CANCEL_H
|
||||||
|
#define CANCEL_H
|
||||||
|
|
||||||
|
#include "libpq-fe.h"
|
||||||
|
|
||||||
|
extern bool CancelRequested;
|
||||||
|
|
||||||
|
extern void SetCancelConn(PGconn *conn);
|
||||||
|
extern void ResetCancelConn(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A callback can be optionally set up to be called at cancellation
|
||||||
|
* time.
|
||||||
|
*/
|
||||||
|
extern void setup_cancel_handler(void (*cancel_callback) (void));
|
||||||
|
|
||||||
|
#endif /* CANCEL_H */
|
@ -142,7 +142,7 @@ sub mkvcbuild
|
|||||||
our @pgcommonbkndfiles = @pgcommonallfiles;
|
our @pgcommonbkndfiles = @pgcommonallfiles;
|
||||||
|
|
||||||
our @pgfeutilsfiles = qw(
|
our @pgfeutilsfiles = qw(
|
||||||
conditional.c mbprint.c print.c psqlscan.l psqlscan.c
|
cancel.c conditional.c mbprint.c print.c psqlscan.l psqlscan.c
|
||||||
simple_list.c string_utils.c recovery_gen.c);
|
simple_list.c string_utils.c recovery_gen.c);
|
||||||
|
|
||||||
$libpgport = $solution->AddProject('libpgport', 'lib', 'misc');
|
$libpgport = $solution->AddProject('libpgport', 'lib', 'misc');
|
||||||
|
Loading…
Reference in New Issue
Block a user