summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Paquier <michael@paquier.xyz>2019-12-02 11:18:56 +0900
committerMichael Paquier <michael@paquier.xyz>2019-12-02 11:18:56 +0900
commita4fd3aa719e8f97299dfcf1a8f79b3017e2b8d8b (patch)
tree5e2abc311e6f09a3788dd2989e8ed56555975291
parentc01ac6dcba0aa65ad237c3af4a67bc70da8e4b0e (diff)
downloadpostgresql-a4fd3aa719e8f97299dfcf1a8f79b3017e2b8d8b.tar.gz
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
-rw-r--r--src/bin/psql/command.c1
-rw-r--r--src/bin/psql/common.c187
-rw-r--r--src/bin/psql/common.h5
-rw-r--r--src/bin/psql/large_obj.c7
-rw-r--r--src/bin/psql/startup.c2
-rw-r--r--src/bin/scripts/clusterdb.c2
-rw-r--r--src/bin/scripts/common.c147
-rw-r--r--src/bin/scripts/common.h7
-rw-r--r--src/bin/scripts/reindexdb.c2
-rw-r--r--src/bin/scripts/vacuumdb.c2
-rw-r--r--src/fe_utils/Makefile1
-rw-r--r--src/fe_utils/cancel.c225
-rw-r--r--src/include/fe_utils/cancel.h30
-rw-r--r--src/tools/msvc/Mkvcbuild.pm2
14 files changed, 294 insertions, 326 deletions
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 0a2597046d..48b6279403 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -29,6 +29,7 @@
#include "copy.h"
#include "crosstabview.h"
#include "describe.h"
+#include "fe_utils/cancel.h"
#include "fe_utils/print.h"
#include "fe_utils/string_utils.h"
#include "help.h"
diff --git a/src/bin/psql/common.c b/src/bin/psql/common.c
index 53a1ea2bdb..9c0d53689e 100644
--- a/src/bin/psql/common.c
+++ b/src/bin/psql/common.c
@@ -23,6 +23,7 @@
#include "common/logging.h"
#include "copy.h"
#include "crosstabview.h"
+#include "fe_utils/cancel.h"
#include "fe_utils/mbprint.h"
#include "fe_utils/string_utils.h"
#include "portability/instr_time.h"
@@ -228,58 +229,28 @@ NoticeProcessor(void *arg, const char *message)
* Code to support query cancellation
*
* 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
- * 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.
+ * cancel request to the backend.
*
* SIGINT is supposed to abort all long-running psql operations, not only
* 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
* set sigint_interrupt_enabled true while blocked, instructing the signal
* catcher to longjmp through sigint_interrupt_jmp. We assume readline and
- * fgets are coded to handle possible interruption. (XXX currently this does
- * not work on win32, so control-C is less useful there)
+ * fgets are coded to handle possible interruption.
+ *
+ * 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;
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
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 (sigint_interrupt_enabled)
{
@@ -288,74 +259,24 @@ handle_sigint(SIGNAL_ARGS)
}
/* else, set cancel flag to stop any long-running loops */
- cancel_pressed = 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 */
+ CancelRequested = true;
}
-void
-setup_cancel_handler(void)
-{
- pqsignal(SIGINT, handle_sigint);
-}
-#else /* WIN32 */
+#else
-static BOOL WINAPI
-consoleHandler(DWORD dwCtrlType)
+static void
+psql_cancel_callback(void)
{
- 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;
+ /* nothing to do here */
}
+#endif /* !WIN32 */
+
void
-setup_cancel_handler(void)
+psql_setup_cancel_handler(void)
{
- InitializeCriticalSection(&cancelConnLock);
-
- SetConsoleCtrlHandler(consoleHandler, TRUE);
+ setup_cancel_handler(psql_cancel_callback);
}
-#endif /* WIN32 */
/* 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
@@ -707,7 +572,7 @@ PSQLexec(const char *query)
return NULL;
}
- SetCancelConn();
+ SetCancelConn(pset.db);
res = PQexec(pset.db, query);
@@ -746,7 +611,7 @@ PSQLexecWatch(const char *query, const printQueryOpt *opt)
return 0;
}
- SetCancelConn();
+ SetCancelConn(pset.db);
if (pset.timing)
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
* process, so detect a sent cancellation request and exit in this case.
*/
- if (cancel_pressed)
+ if (CancelRequested)
{
PQclear(res);
return 0;
@@ -973,8 +838,8 @@ ExecQueryTuples(const PGresult *result)
{
const char *query = PQgetvalue(result, r, c);
- /* Abandon execution if cancel_pressed */
- if (cancel_pressed)
+ /* Abandon execution if CancelRequested */
+ if (CancelRequested)
goto loop_exit;
/*
@@ -1091,7 +956,7 @@ ProcessResult(PGresult **results)
FILE *copystream;
PGresult *copy_result;
- SetCancelConn();
+ SetCancelConn(pset.db);
if (result_status == PGRES_COPY_OUT)
{
bool need_close = false;
@@ -1342,7 +1207,7 @@ SendQuery(const char *query)
if (fgets(buf, sizeof(buf), stdin) != NULL)
if (buf[0] == 'x')
goto sendquery_cleanup;
- if (cancel_pressed)
+ if (CancelRequested)
goto sendquery_cleanup;
}
else if (pset.echo == PSQL_ECHO_QUERIES)
@@ -1360,7 +1225,7 @@ SendQuery(const char *query)
fflush(pset.logfile);
}
- SetCancelConn();
+ SetCancelConn(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
* stop bothering to pull down more data.
*/
- if (ntuples < fetch_count || cancel_pressed || flush_error ||
+ if (ntuples < fetch_count || CancelRequested || flush_error ||
ferror(fout))
break;
}
diff --git a/src/bin/psql/common.h b/src/bin/psql/common.h
index 292dc54919..60ff9f1758 100644
--- a/src/bin/psql/common.h
+++ b/src/bin/psql/common.h
@@ -26,10 +26,7 @@ extern volatile bool sigint_interrupt_enabled;
extern sigjmp_buf sigint_interrupt_jmp;
-extern void setup_cancel_handler(void);
-
-extern void SetCancelConn(void);
-extern void ResetCancelConn(void);
+extern void psql_setup_cancel_handler(void);
extern PGresult *PSQLexec(const char *query);
extern int PSQLexecWatch(const char *query, const printQueryOpt *opt);
diff --git a/src/bin/psql/large_obj.c b/src/bin/psql/large_obj.c
index 042403e0f7..75b733c3c8 100644
--- a/src/bin/psql/large_obj.c
+++ b/src/bin/psql/large_obj.c
@@ -9,6 +9,7 @@
#include "common.h"
#include "common/logging.h"
+#include "fe_utils/cancel.h"
#include "large_obj.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))
return false;
- SetCancelConn();
+ SetCancelConn(NULL);
status = lo_export(pset.db, atooid(loid_arg), filename_arg);
ResetCancelConn();
@@ -182,7 +183,7 @@ do_lo_import(const char *filename_arg, const char *comment_arg)
if (!start_lo_xact("\\lo_import", &own_transaction))
return false;
- SetCancelConn();
+ SetCancelConn(NULL);
loid = lo_import(pset.db, filename_arg);
ResetCancelConn();
@@ -244,7 +245,7 @@ do_lo_unlink(const char *loid_arg)
if (!start_lo_xact("\\lo_unlink", &own_transaction))
return false;
- SetCancelConn();
+ SetCancelConn(NULL);
status = lo_unlink(pset.db, loid);
ResetCancelConn();
diff --git a/src/bin/psql/startup.c b/src/bin/psql/startup.c
index 0d941ef5ba..43cf139a31 100644
--- a/src/bin/psql/startup.c
+++ b/src/bin/psql/startup.c
@@ -301,7 +301,7 @@ main(int argc, char *argv[])
exit(EXIT_BADCONN);
}
- setup_cancel_handler();
+ psql_setup_cancel_handler();
PQsetNoticeProcessor(pset.db, NoticeProcessor, NULL);
diff --git a/src/bin/scripts/clusterdb.c b/src/bin/scripts/clusterdb.c
index d380127356..3aee5f2834 100644
--- a/src/bin/scripts/clusterdb.c
+++ b/src/bin/scripts/clusterdb.c
@@ -133,7 +133,7 @@ main(int argc, char *argv[])
exit(1);
}
- setup_cancel_handler();
+ setup_cancel_handler(NULL);
if (alldb)
{
diff --git a/src/bin/scripts/common.c b/src/bin/scripts/common.c
index 1b38a1da49..d2a7547441 100644
--- a/src/bin/scripts/common.c
+++ b/src/bin/scripts/common.c
@@ -24,14 +24,6 @@
#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
* options.
@@ -465,142 +457,3 @@ yesno_prompt(const char *question)
_(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 */
diff --git a/src/bin/scripts/common.h b/src/bin/scripts/common.h
index b8580f927a..db2f85b472 100644
--- a/src/bin/scripts/common.h
+++ b/src/bin/scripts/common.h
@@ -10,6 +10,7 @@
#define COMMON_H
#include "common/username.h"
+#include "fe_utils/cancel.h"
#include "getopt_long.h" /* pgrminclude ignore */
#include "libpq-fe.h"
#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 void setup_cancel_handler(void);
-
-extern void SetCancelConn(PGconn *conn);
-extern void ResetCancelConn(void);
-
-
#endif /* COMMON_H */
diff --git a/src/bin/scripts/reindexdb.c b/src/bin/scripts/reindexdb.c
index f00aec15de..bedd95cf9d 100644
--- a/src/bin/scripts/reindexdb.c
+++ b/src/bin/scripts/reindexdb.c
@@ -187,7 +187,7 @@ main(int argc, char *argv[])
exit(1);
}
- setup_cancel_handler();
+ setup_cancel_handler(NULL);
if (alldb)
{
diff --git a/src/bin/scripts/vacuumdb.c b/src/bin/scripts/vacuumdb.c
index 2c7219239f..83a94dc632 100644
--- a/src/bin/scripts/vacuumdb.c
+++ b/src/bin/scripts/vacuumdb.c
@@ -257,7 +257,7 @@ main(int argc, char *argv[])
/* allow 'and_analyze' with 'analyze_only' */
}
- setup_cancel_handler();
+ setup_cancel_handler(NULL);
/* Avoid opening extra connections. */
if (tbl_count && (concurrentCons > tbl_count))
diff --git a/src/fe_utils/Makefile b/src/fe_utils/Makefile
index beea506536..e029333194 100644
--- a/src/fe_utils/Makefile
+++ b/src/fe_utils/Makefile
@@ -20,6 +20,7 @@ include $(top_builddir)/src/Makefile.global
override CPPFLAGS := -DFRONTEND -I$(libpq_srcdir) $(CPPFLAGS)
OBJS = \
+ cancel.o \
conditional.o \
mbprint.o \
print.o \
diff --git a/src/fe_utils/cancel.c b/src/fe_utils/cancel.c
new file mode 100644
index 0000000000..04e0d1e3b2
--- /dev/null
+++ b/src/fe_utils/cancel.c
@@ -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 */
diff --git a/src/include/fe_utils/cancel.h b/src/include/fe_utils/cancel.h
new file mode 100644
index 0000000000..959a38acf3
--- /dev/null
+++ b/src/include/fe_utils/cancel.h
@@ -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 */
diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm
index 9a0963a050..afa29b5ff8 100644
--- a/src/tools/msvc/Mkvcbuild.pm
+++ b/src/tools/msvc/Mkvcbuild.pm
@@ -142,7 +142,7 @@ sub mkvcbuild
our @pgcommonbkndfiles = @pgcommonallfiles;
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);
$libpgport = $solution->AddProject('libpgport', 'lib', 'misc');