Refactor query cancellation code into src/fe_utils/
authorMichael Paquier <michael@paquier.xyz>
Mon, 2 Dec 2019 02:18:56 +0000 (11:18 +0900)
committerMichael Paquier <michael@paquier.xyz>
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');