Refactor query cancellation code into src/fe_utils/
authorMichael Paquier <[email protected]>
Mon, 2 Dec 2019 02:18:56 +0000 (11:18 +0900)
committerMichael Paquier <[email protected]>
Mon, 2 Dec 2019 02:18:56 +0000 (11:18 +0900)
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

14 files changed:
src/bin/psql/command.c
src/bin/psql/common.c
src/bin/psql/common.h
src/bin/psql/large_obj.c
src/bin/psql/startup.c
src/bin/scripts/clusterdb.c
src/bin/scripts/common.c
src/bin/scripts/common.h
src/bin/scripts/reindexdb.c
src/bin/scripts/vacuumdb.c
src/fe_utils/Makefile
src/fe_utils/cancel.c[new file with mode: 0644]
src/include/fe_utils/cancel.h[new file with mode: 0644]
src/tools/msvc/Mkvcbuild.pm

index 0a2597046d6647e750ca0740dd6d8ff02dbec045..48b627940342714d9a7fd0679f7b06da971e88ae 100644 (file)
@@ -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"
index 53a1ea2bdb9728ecd19fc056043be00615492dff..9c0d53689ec4d519b25837d7448ee26f9c45ea8a 100644 (file)
@@ -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;
        }
index 292dc549190006c9a99619118ca731549c82cbb6..60ff9f1758ec2793ccdec863d23e2149475ee49e 100644 (file)
@@ -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);
index 042403e0f78d6d3e620ef8f1e96e7ed0f910b738..75b733c3c8a059c9cfe9bd0c9588ba11fd19f002 100644 (file)
@@ -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();
 
index 0d941ef5ba56c529e32cf7496e5d7bb374f13dbc..43cf139a31e6967610b21cda7b60758694c74302 100644 (file)
@@ -301,7 +301,7 @@ main(int argc, char *argv[])
                exit(EXIT_BADCONN);
        }
 
-       setup_cancel_handler();
+       psql_setup_cancel_handler();
 
        PQsetNoticeProcessor(pset.db, NoticeProcessor, NULL);
 
index d380127356692155ee26329d20206b9c6cec6ecc..3aee5f28349d70ad4d1303a24a0e10cdfb6c6bd9 100644 (file)
@@ -133,7 +133,7 @@ main(int argc, char *argv[])
                exit(1);
        }
 
-       setup_cancel_handler();
+       setup_cancel_handler(NULL);
 
        if (alldb)
        {
index 1b38a1da4949ae5d52fcf8602d87eba169ab90e8..d2a7547441a348f5e26f959abda3971c404323d3 100644 (file)
 
 #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 */
index b8580f927a5fc1530c6e00ffdeb7598189ad9281..db2f85b4720a914d73168a1f85eddbc30784ebbb 100644 (file)
@@ -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 */
index f00aec15de35e0c5d920fd7302c9a498b27dd37d..bedd95cf9df960b6241417ab282d1208e5cf6a04 100644 (file)
@@ -187,7 +187,7 @@ main(int argc, char *argv[])
                exit(1);
        }
 
-       setup_cancel_handler();
+       setup_cancel_handler(NULL);
 
        if (alldb)
        {
index 2c7219239f98396aaa1835d23b60159a5606c75f..83a94dc632f39cc1747ca627dace9a211da1ce60 100644 (file)
@@ -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))
index beea50653650d5d272649c6b4805806b0ab21ea3..e029333194c04637a1c1f27137817b9b6ec5813c 100644 (file)
@@ -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 (file)
index 0000000..04e0d1e
--- /dev/null
@@ -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 (file)
index 0000000..959a38a
--- /dev/null
@@ -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 */
index 9a0963a050d97c16a8744ddadf5bf371defb3440..afa29b5ff8bc76ce1294576691b866c7508c5894 100644 (file)
@@ -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');