]> granicus.if.org Git - postgresql/commitdiff
Clean up psql's control-C handling to avoid longjmp'ing out of random
authorTom Lane <tgl@sss.pgh.pa.us>
Wed, 14 Jun 2006 16:49:03 +0000 (16:49 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Wed, 14 Jun 2006 16:49:03 +0000 (16:49 +0000)
places --- that risks corrupting data structures, losing sync with the
backend, etc.  We now longjmp only from calls to readline, fgets, and
fread, which we assume are coded to protect themselves against interrupts
at undesirable times.  This requires adding explicit tests for
cancel_pressed in long-running loops, but on the whole it's far cleaner.
Martijn van Oosterhout and Tom Lane.

12 files changed:
src/bin/psql/common.c
src/bin/psql/common.h
src/bin/psql/copy.c
src/bin/psql/describe.c
src/bin/psql/input.c
src/bin/psql/large_obj.c
src/bin/psql/mainloop.c
src/bin/psql/mainloop.h
src/bin/psql/print.c
src/bin/psql/startup.c
src/bin/psql/variables.c
src/port/sprompt.c

index 6b7f683b055e237ca56931ab5815d6948a80eb1f..c35d3c3eef5d8ab7bca3dd875143492df82f9abf 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 2000-2006, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/bin/psql/common.c,v 1.118 2006/05/26 19:51:29 tgl Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/common.c,v 1.119 2006/06/14 16:49:02 tgl Exp $
  */
 #include "postgres_fe.h"
 #include "common.h"
@@ -16,7 +16,6 @@
 #ifndef WIN32
 #include <sys/time.h>
 #include <unistd.h>                            /* for write() */
-#include <setjmp.h>
 #else
 #include <io.h>                                        /* for _write() */
 #include <win32.h>
@@ -58,8 +57,6 @@ typedef struct _timeb TimevalStruct;
         ((T)->millitm - (U)->millitm))
 #endif
 
-extern bool prompt_state;
-
 
 static bool command_no_begin(const char *query);
 
@@ -219,55 +216,79 @@ NoticeProcessor(void *arg, const char *message)
 /*
  * Code to support query cancellation
  *
- * Before we start a query, we enable a SIGINT signal catcher that sends 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
  * the signal handler is safe because PQcancel() is written to make it
- * so. We use write() to print to stderr because it's better to use simple
+ * so. We use write() to report to stderr because it's better to use simple
  * facilities in a signal handler.
  *
  * On win32, the signal cancelling 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 other
+ * 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
+ * database queries.  In most places, this is accomplished by checking
+ * cancel_pressed 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)
  */
-static PGcancel *cancelConn = NULL;
+volatile bool sigint_interrupt_enabled = false;
+
+sigjmp_buf     sigint_interrupt_jmp;
+
+static PGcancel * volatile cancelConn = NULL;
 
 #ifdef WIN32
 static CRITICAL_SECTION cancelConnLock;
 #endif
 
-volatile bool cancel_pressed = false;
-
 #define write_stderr(str)      write(fileno(stderr), str, strlen(str))
 
 
 #ifndef WIN32
 
-void
+static void
 handle_sigint(SIGNAL_ARGS)
 {
        int                     save_errno = errno;
        char            errbuf[256];
 
-       /* Don't muck around if prompting for a password. */
-       if (prompt_state)
-               return;
-
-       if (cancelConn == NULL)
-               siglongjmp(main_loop_jmp, 1);
+       /* if we are waiting for input, longjmp out of it */
+       if (sigint_interrupt_enabled)
+       {
+               sigint_interrupt_enabled = false;
+               siglongjmp(sigint_interrupt_jmp, 1);
+       }
 
+       /* else, set cancel flag to stop any long-running loops */
        cancel_pressed = true;
 
-       if (PQcancel(cancelConn, errbuf, sizeof(errbuf)))
-               write_stderr("Cancel request sent\n");
-       else
+       /* and send QueryCancel if we are processing a database query */
+       if (cancelConn != NULL)
        {
-               write_stderr("Could not send cancel request: ");
-               write_stderr(errbuf);
+               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 */
 }
+
+void
+setup_cancel_handler(void)
+{
+       pqsignal(SIGINT, handle_sigint);
+}
+
 #else                                                  /* WIN32 */
 
 static BOOL WINAPI
@@ -278,15 +299,17 @@ consoleHandler(DWORD dwCtrlType)
        if (dwCtrlType == CTRL_C_EVENT ||
                dwCtrlType == CTRL_BREAK_EVENT)
        {
-               if (prompt_state)
-                       return TRUE;
+               /*
+                * Can't longjmp here, because we are in wrong thread :-(
+                */
+
+               /* set cancel flag to stop any long-running loops */
+               cancel_pressed = true;
 
-               /* Perform query cancel */
+               /* and send QueryCancel if we are processing a database query */
                EnterCriticalSection(&cancelConnLock);
                if (cancelConn != NULL)
                {
-                       cancel_pressed = true;
-
                        if (PQcancel(cancelConn, errbuf, sizeof(errbuf)))
                                write_stderr("Cancel request sent\n");
                        else
@@ -304,24 +327,14 @@ consoleHandler(DWORD dwCtrlType)
                return FALSE;
 }
 
-void
-setup_win32_locks(void)
-{
-       InitializeCriticalSection(&cancelConnLock);
-}
-
 void
 setup_cancel_handler(void)
 {
-       static bool done = false;
+       InitializeCriticalSection(&cancelConnLock);
 
-       /* only need one handler per process */
-       if (!done)
-       {
-               SetConsoleCtrlHandler(consoleHandler, TRUE);
-               done = true;
-       }
+       SetConsoleCtrlHandler(consoleHandler, TRUE);
 }
+
 #endif   /* WIN32 */
 
 
@@ -386,16 +399,22 @@ CheckConnection(void)
  *
  * Set cancelConn to point to the current database connection.
  */
-static void
+void
 SetCancelConn(void)
 {
+       PGcancel *oldCancelConn;
+
 #ifdef WIN32
        EnterCriticalSection(&cancelConnLock);
 #endif
 
        /* Free the old one if we have one */
-       if (cancelConn != NULL)
-               PQfreeCancel(cancelConn);
+       oldCancelConn = cancelConn;
+       /* be sure handle_sigint doesn't use pointer while freeing */
+       cancelConn = NULL;
+
+       if (oldCancelConn != NULL)
+               PQfreeCancel(oldCancelConn);
 
        cancelConn = PQgetCancel(pset.db);
 
@@ -413,15 +432,19 @@ SetCancelConn(void)
 void
 ResetCancelConn(void)
 {
+       PGcancel *oldCancelConn;
+
 #ifdef WIN32
        EnterCriticalSection(&cancelConnLock);
 #endif
 
-       if (cancelConn)
-               PQfreeCancel(cancelConn);
-
+       oldCancelConn = cancelConn;
+       /* be sure handle_sigint doesn't use pointer while freeing */
        cancelConn = NULL;
 
+       if (oldCancelConn != NULL)
+               PQfreeCancel(oldCancelConn);
+
 #ifdef WIN32
        LeaveCriticalSection(&cancelConnLock);
 #endif
@@ -453,15 +476,8 @@ AcceptResult(const PGresult *result, const char *query)
                        case PGRES_TUPLES_OK:
                        case PGRES_EMPTY_QUERY:
                        case PGRES_COPY_IN:
-                               /* Fine, do nothing */
-                               break;
-
                        case PGRES_COPY_OUT:
-                               /*
-                                * Keep cancel connection active during copy out state.
-                                * The matching ResetCancelConn() is in handleCopyOut.
-                                */
-                               SetCancelConn();
+                               /* Fine, do nothing */
                                break;
 
                        default:
@@ -648,12 +664,16 @@ ProcessCopyResult(PGresult *results)
                        break;
 
                case PGRES_COPY_OUT:
+                       SetCancelConn();
                        success = handleCopyOut(pset.db, pset.queryFout);
+                       ResetCancelConn();
                        break;
 
                case PGRES_COPY_IN:
+                       SetCancelConn();
                        success = handleCopyIn(pset.db, pset.cur_cmd_source,
                                                                   PQbinaryTuples(results));
+                       ResetCancelConn();
                        break;
 
                default:
index 4cf5da0bf351b469c5e78ae74a52a6048184d2bb..6045b7587d8408e78a54719d9e3e09f5dcd4e2fe 100644 (file)
@@ -3,14 +3,13 @@
  *
  * Copyright (c) 2000-2006, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/bin/psql/common.h,v 1.49 2006/06/01 00:15:36 tgl Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/common.h,v 1.50 2006/06/14 16:49:02 tgl Exp $
  */
 #ifndef COMMON_H
 #define COMMON_H
 
 #include "postgres_fe.h"
-#include <signal.h>
-#include "pqsignal.h"
+#include <setjmp.h>
 #include "libpq-fe.h"
 
 #ifdef USE_ASSERT_CHECKING
@@ -41,16 +40,17 @@ __attribute__((format(printf, 1, 2)));
 
 extern void NoticeProcessor(void *arg, const char *message);
 
-extern volatile bool cancel_pressed;
+extern volatile bool sigint_interrupt_enabled;
 
-extern void ResetCancelConn(void);
+extern sigjmp_buf sigint_interrupt_jmp;
+
+extern volatile bool cancel_pressed;
+/* Note: cancel_pressed is defined in print.c, see that file for reasons */
 
-#ifndef WIN32
-extern void handle_sigint(SIGNAL_ARGS);
-#else
-extern void setup_win32_locks(void);
 extern void setup_cancel_handler(void);
-#endif
+
+extern void SetCancelConn(void);
+extern void ResetCancelConn(void);
 
 extern PGresult *PSQLexec(const char *query, bool start_xact);
 
index abf4b6034225c214d288382319195986038bb0fd..2ec01af186bd82188c1bc7d7bfbb09ae7cccaa14 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 2000-2006, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/bin/psql/copy.c,v 1.65 2006/06/07 22:24:45 momjian Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/copy.c,v 1.66 2006/06/14 16:49:02 tgl Exp $
  */
 #include "postgres_fe.h"
 #include "copy.h"
@@ -550,11 +550,15 @@ do_copy(const char *args)
        switch (PQresultStatus(result))
        {
                case PGRES_COPY_OUT:
+                       SetCancelConn();
                        success = handleCopyOut(pset.db, copystream);
+                       ResetCancelConn();
                        break;
                case PGRES_COPY_IN:
+                       SetCancelConn();
                        success = handleCopyIn(pset.db, copystream,
                                                                   PQbinaryTuples(result));
+                       ResetCancelConn();
                        break;
                case PGRES_NONFATAL_ERROR:
                case PGRES_FATAL_ERROR:
@@ -650,9 +654,6 @@ handleCopyOut(PGconn *conn, FILE *copystream)
                OK = false;
        }
        PQclear(res);
-
-       /* Disable cancel connection (see AcceptResult in common.c) */
-       ResetCancelConn();
        
        return OK;
 }
@@ -675,11 +676,31 @@ handleCopyOut(PGconn *conn, FILE *copystream)
 bool
 handleCopyIn(PGconn *conn, FILE *copystream, bool isbinary)
 {
-       bool            OK = true;
+       bool            OK;
        const char *prompt;
        char            buf[COPYBUFSIZ];
        PGresult *res;
 
+       /*
+        * Establish longjmp destination for exiting from wait-for-input.
+        * (This is only effective while sigint_interrupt_enabled is TRUE.)
+        */
+       if (sigsetjmp(sigint_interrupt_jmp, 1) != 0)
+       {
+               /* got here with longjmp */
+
+               /* Terminate data transfer */
+               PQputCopyEnd(conn, _("aborted by user cancel"));
+
+               /* Check command status and return to normal libpq state */
+               res = PQgetResult(conn);
+               if (PQresultStatus(res) != PGRES_COMMAND_OK)
+                       psql_error("%s", PQerrorMessage(conn));
+               PQclear(res);
+
+               return false;
+       }
+
        /* Prompt if interactive input */
        if (isatty(fileno(copystream)))
        {
@@ -691,10 +712,10 @@ handleCopyIn(PGconn *conn, FILE *copystream, bool isbinary)
        else
                prompt = NULL;
 
+       OK = true;
+
        if (isbinary)
        {
-           int buflen;
-
                /* interactive input probably silly, but give one prompt anyway */
                if (prompt)
                {
@@ -702,8 +723,20 @@ handleCopyIn(PGconn *conn, FILE *copystream, bool isbinary)
                        fflush(stdout);
                }
 
-               while ((buflen = fread(buf, 1, COPYBUFSIZ, copystream)) > 0)
+               for (;;)
                {
+                       int buflen;
+
+                       /* enable longjmp while waiting for input */
+                       sigint_interrupt_enabled = true;
+
+                       buflen = fread(buf, 1, COPYBUFSIZ, copystream);
+
+                       sigint_interrupt_enabled = false;
+
+                       if (buflen <= 0)
+                               break;
+
                        if (PQputCopyData(conn, buf, buflen) <= 0)
                        {
                                OK = false;
@@ -732,8 +765,16 @@ handleCopyIn(PGconn *conn, FILE *copystream, bool isbinary)
                        while (!linedone)
                        {                                               /* for each bufferload in line ... */
                                int             linelen;
+                               char   *fgresult;
+
+                               /* enable longjmp while waiting for input */
+                               sigint_interrupt_enabled = true;
+
+                               fgresult = fgets(buf, COPYBUFSIZ, copystream);
+
+                               sigint_interrupt_enabled = false;
 
-                               if (!fgets(buf, COPYBUFSIZ, copystream))
+                               if (!fgresult)
                                {
                                        copydone = true;
                                        break;
index 9c9f60940791c6df3bdcb6dd84adf2e1b527dbbb..7053f5beffe297dbc8bde72bfe22664e3a8d4dd0 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 2000-2006, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/bin/psql/describe.c,v 1.139 2006/06/01 00:15:36 tgl Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/describe.c,v 1.140 2006/06/14 16:49:02 tgl Exp $
  */
 #include "postgres_fe.h"
 #include "describe.h"
@@ -663,6 +663,11 @@ describeTableDetails(const char *pattern, bool verbose)
                        PQclear(res);
                        return false;
                }
+               if (cancel_pressed)
+               {
+                       PQclear(res);
+                       return false;
+               }
        }
 
        PQclear(res);
index b970175b805bb62376c7fb179406074079cc0376..266986a886d1315099b12cc33353693ace2bb4f2 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 2000-2006, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/bin/psql/input.c,v 1.54 2006/06/11 23:06:00 tgl Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/input.c,v 1.55 2006/06/14 16:49:02 tgl Exp $
  */
 #include "postgres_fe.h"
 
@@ -82,7 +82,9 @@ GetHistControlConfig(void)
  * gets_interactive()
  *
  * Gets a line of interactive input, using readline if desired.
- * The result is malloc'ed.
+ * The result is a malloc'd string.
+ *
+ * Caller *must* have set up sigint_interrupt_jmp before calling.
  */
 char *
 gets_interactive(const char *prompt)
@@ -90,8 +92,18 @@ gets_interactive(const char *prompt)
 #ifdef USE_READLINE
        if (useReadline)
        {
+               char   *result;
+
+               /* Enable SIGINT to longjmp to sigint_interrupt_jmp */
+               sigint_interrupt_enabled = true;
+
                /* On some platforms, readline is declared as readline(char *) */
-               return readline((char *) prompt);
+               result = readline((char *) prompt);
+
+               /* Disable SIGINT again */
+               sigint_interrupt_enabled = false;
+
+               return result;
        }
 #endif
 
@@ -169,30 +181,56 @@ pg_send_history(PQExpBuffer history_buf)
  * gets_fromFile
  *
  * Gets a line of noninteractive input from a file (which could be stdin).
+ * The result is a malloc'd string.
+ *
+ * Caller *must* have set up sigint_interrupt_jmp before calling.
+ *
+ * Note: we re-use a static PQExpBuffer for each call.  This is to avoid
+ * leaking memory if interrupted by SIGINT.
  */
 char *
 gets_fromFile(FILE *source)
 {
-       PQExpBufferData buffer;
+       static PQExpBuffer buffer = NULL;
+
        char            line[1024];
 
-       initPQExpBuffer(&buffer);
+       if (buffer == NULL)                     /* first time through? */
+               buffer = createPQExpBuffer();
+       else
+               resetPQExpBuffer(buffer);
 
-       while (fgets(line, sizeof(line), source) != NULL)
+       for (;;)
        {
-               appendPQExpBufferStr(&buffer, line);
-               if (buffer.data[buffer.len - 1] == '\n')
+               char   *result;
+
+               /* Enable SIGINT to longjmp to sigint_interrupt_jmp */
+               sigint_interrupt_enabled = true;
+
+               /* Get some data */
+               result = fgets(line, sizeof(line), source);
+
+               /* Disable SIGINT again */
+               sigint_interrupt_enabled = false;
+
+               /* EOF? */
+               if (result == NULL)
+                       break;
+
+               appendPQExpBufferStr(buffer, line);
+
+               /* EOL? */
+               if (buffer->data[buffer->len - 1] == '\n')
                {
-                       buffer.data[buffer.len - 1] = '\0';
-                       return buffer.data;
+                       buffer->data[buffer->len - 1] = '\0';
+                       return pg_strdup(buffer->data);
                }
        }
 
-       if (buffer.len > 0)
-               return buffer.data;             /* EOF after reading some bufferload(s) */
+       if (buffer->len > 0)            /* EOF after reading some bufferload(s) */
+               return pg_strdup(buffer->data);
 
        /* EOF, so return null */
-       termPQExpBuffer(&buffer);
        return NULL;
 }
 
index 5de0348b397c864ef2c85781632ed43640b05396..9e8d6d2945dc2ed8e2a81b136dd370e6982f98c1 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 2000-2006, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/bin/psql/large_obj.c,v 1.43 2006/05/28 21:13:54 tgl Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/large_obj.c,v 1.44 2006/06/14 16:49:02 tgl Exp $
  */
 #include "postgres_fe.h"
 #include "large_obj.h"
@@ -120,10 +120,13 @@ do_lo_export(const char *loid_arg, const char *filename_arg)
        if (!start_lo_xact("\\lo_export", &own_transaction))
                return false;
 
+       SetCancelConn();
        status = lo_export(pset.db, atooid(loid_arg), filename_arg);
+       ResetCancelConn();
+
+       /* of course this status is documented nowhere :( */
        if (status != 1)
-       {                                                       /* of course this status is documented nowhere
-                                                                * :( */
+       {
                fputs(PQerrorMessage(pset.db), stderr);
                return fail_lo_xact("\\lo_export", own_transaction);
        }
@@ -153,7 +156,10 @@ do_lo_import(const char *filename_arg, const char *comment_arg)
        if (!start_lo_xact("\\lo_import", &own_transaction))
                return false;
 
+       SetCancelConn();
        loid = lo_import(pset.db, filename_arg);
+       ResetCancelConn();
+
        if (loid == InvalidOid)
        {
                fputs(PQerrorMessage(pset.db), stderr);
@@ -211,7 +217,10 @@ do_lo_unlink(const char *loid_arg)
        if (!start_lo_xact("\\lo_unlink", &own_transaction))
                return false;
 
+       SetCancelConn();
        status = lo_unlink(pset.db, loid);
+       ResetCancelConn();
+
        if (status == -1)
        {
                fputs(PQerrorMessage(pset.db), stderr);
index af297ae2a91c5a13a9520f1b1ab5c416ad40c8c0..b7022281e54747418a7056ae4665f69fe5c6bb62 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 2000-2006, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/bin/psql/mainloop.c,v 1.79 2006/06/11 23:06:00 tgl Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/mainloop.c,v 1.80 2006/06/14 16:49:02 tgl Exp $
  */
 #include "postgres_fe.h"
 #include "mainloop.h"
 #include "psqlscan.h"
 #include "settings.h"
 
-#ifndef WIN32
-#include <setjmp.h>
-sigjmp_buf     main_loop_jmp;
-#endif
-
 
 /*
  * Main processing loop for reading lines of input
@@ -80,16 +75,14 @@ MainLoop(FILE *source)
        while (successResult == EXIT_SUCCESS)
        {
                /*
-                * Welcome code for Control-C
+                * Clean up after a previous Control-C
                 */
                if (cancel_pressed)
                {
                        if (!pset.cur_cmd_interactive)
                        {
                                /*
-                                * You get here if you stopped a script with Ctrl-C and a
-                                * query cancel was issued. In that case we don't do the
-                                * longjmp, so the query routine can finish nicely.
+                                * You get here if you stopped a script with Ctrl-C.
                                 */
                                successResult = EXIT_USER;
                                break;
@@ -98,18 +91,24 @@ MainLoop(FILE *source)
                        cancel_pressed = false;
                }
 
-#ifndef WIN32
-               if (sigsetjmp(main_loop_jmp, 1) != 0)
+               /*
+                * Establish longjmp destination for exiting from wait-for-input.
+                * We must re-do this each time through the loop for safety, since
+                * the jmpbuf might get changed during command execution.
+                */
+               if (sigsetjmp(sigint_interrupt_jmp, 1) != 0)
                {
                        /* got here with longjmp */
 
                        /* reset parsing state */
-                       resetPQExpBuffer(query_buf);
                        psql_scan_finish(scan_state);
                        psql_scan_reset(scan_state);
+                       resetPQExpBuffer(query_buf);
+                       resetPQExpBuffer(history_buf);
                        count_eof = 0;
                        slashCmdStatus = PSQL_CMD_UNKNOWN;
                        prompt_status = PROMPT_READY;
+                       cancel_pressed = false;
 
                        if (pset.cur_cmd_interactive)
                                putc('\n', stdout);
@@ -120,14 +119,6 @@ MainLoop(FILE *source)
                        }
                }
 
-               /*
-                * establish the control-C handler only after main_loop_jmp is ready
-                */
-               pqsignal(SIGINT, handle_sigint);                /* control-C => cancel */
-#else                                                  /* WIN32 */
-               setup_cancel_handler();
-#endif
-
                fflush(stdout);
 
                /*
@@ -360,14 +351,13 @@ MainLoop(FILE *source)
        }
 
        /*
-        * Reset SIGINT handler because main_loop_jmp will be invalid as soon as
-        * we exit this routine.  If there is an outer MainLoop instance, it will
-        * re-enable ^C catching as soon as it gets back to the top of its loop
-        * and resets main_loop_jmp to point to itself.
+        * Let's just make real sure the SIGINT handler won't try to use
+        * sigint_interrupt_jmp after we exit this routine.  If there is an outer
+        * MainLoop instance, it will reset sigint_interrupt_jmp to point to
+        * itself at the top of its loop, before any further interactive input
+        * happens.
         */
-#ifndef WIN32
-       pqsignal(SIGINT, SIG_DFL);
-#endif
+       sigint_interrupt_enabled = false;
 
        destroyPQExpBuffer(query_buf);
        destroyPQExpBuffer(previous_buf);
index 42ba65303f7cc6215cb43a86c965fafac7d16fe3..eedb48f3e70cd1ea77d843fb814574b9f40fb913 100644 (file)
@@ -3,18 +3,13 @@
  *
  * Copyright (c) 2000-2006, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/bin/psql/mainloop.h,v 1.18 2006/03/05 15:58:51 momjian Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/mainloop.h,v 1.19 2006/06/14 16:49:02 tgl Exp $
  */
 #ifndef MAINLOOP_H
 #define MAINLOOP_H
 
 #include "postgres_fe.h"
 #include <stdio.h>
-#ifndef WIN32
-#include <setjmp.h>
-
-extern sigjmp_buf main_loop_jmp;
-#endif
 
 int                    MainLoop(FILE *source);
 
index 1ad04783d0c3018441c8aeca74abaec5bb67f994..97f6f1e665c717986c1c38d80f4e6700920ec589 100644 (file)
@@ -3,11 +3,15 @@
  *
  * Copyright (c) 2000-2006, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/bin/psql/print.c,v 1.86 2006/06/07 22:24:45 momjian Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/print.c,v 1.87 2006/06/14 16:49:02 tgl Exp $
+ *
+ * Note: we include postgres.h not postgres_fe.h so that we can include
+ * catalog/pg_type.h, and thereby have access to INT4OID and similar macros.
  */
-#include "postgres_fe.h"
+#include "postgres.h"
 #include "common.h"
 #include "print.h"
+#include "catalog/pg_type.h"
 
 #include <math.h>
 #include <signal.h>
 
 #include "mbprint.h"
 
+/*
+ * We define the cancel_pressed flag in this file, rather than common.c where
+ * it naturally belongs, because this file is also used by non-psql programs
+ * (see the bin/scripts/ directory).  In those programs cancel_pressed will
+ * never become set and will have no effect.
+ *
+ * Note: print.c's general strategy for when to check cancel_pressed is to do
+ * so at completion of each row of output.
+ */
+volatile bool cancel_pressed = false;
+
 static char *decimal_point;
 static char *grouping;
 static char *thousands_sep;
@@ -171,6 +186,9 @@ print_unaligned_text(const char *title, const char *const * headers,
        const char *const * ptr;
        bool            need_recordsep = false;
 
+       if (cancel_pressed)
+               return;
+
        if (!opt_fieldsep)
                opt_fieldsep = "";
        if (!opt_recordsep)
@@ -202,6 +220,8 @@ print_unaligned_text(const char *title, const char *const * headers,
                {
                        fputs(opt_recordsep, fout);
                        need_recordsep = false;
+                       if (cancel_pressed)
+                               break;
                }
                if (opt_align[i % col_count] == 'r' && opt_numeric_locale)
                {
@@ -222,7 +242,7 @@ print_unaligned_text(const char *title, const char *const * headers,
 
        /* print footers */
 
-       if (!opt_tuples_only && footers)
+       if (!opt_tuples_only && footers && !cancel_pressed)
                for (ptr = footers; *ptr; ptr++)
                {
                        if (need_recordsep)
@@ -252,6 +272,9 @@ print_unaligned_vertical(const char *title, const char *const * headers,
        unsigned int i;
        const char *const * ptr;
 
+       if (cancel_pressed)
+               return;
+
        if (!opt_fieldsep)
                opt_fieldsep = "";
        if (!opt_recordsep)
@@ -273,6 +296,8 @@ print_unaligned_vertical(const char *title, const char *const * headers,
                        fputs(opt_recordsep, fout);
                        if (i % col_count == 0)
                                fputs(opt_recordsep, fout);             /* another one */
+                       if (cancel_pressed)
+                               break;
                }
 
                fputs(headers[i % col_count], fout);
@@ -289,7 +314,7 @@ print_unaligned_vertical(const char *title, const char *const * headers,
        }
 
        /* print footers */
-       if (!opt_tuples_only && footers && *footers)
+       if (!opt_tuples_only && footers && *footers && !cancel_pressed)
        {
                fputs(opt_recordsep, fout);
                for (ptr = footers; *ptr; ptr++)
@@ -369,6 +394,9 @@ print_aligned_text(const char *title, const char *const * headers,
        
        int *complete;                  /* Array remembering which columns have completed output */
 
+       if (cancel_pressed)
+               return;
+
        /* count columns */
        for (ptr = headers; *ptr; ptr++)
                col_count++;
@@ -543,7 +571,6 @@ print_aligned_text(const char *title, const char *const * headers,
                        fputc('\n', fout);
                }
 
-
                _print_horizontal_line(col_count, widths, opt_border, fout);
        }
 
@@ -553,7 +580,10 @@ print_aligned_text(const char *title, const char *const * headers,
                int j;
                int cols_todo = col_count;
                int line_count;                 /* Number of lines output so far in row */
-               
+
+               if (cancel_pressed)
+                       break;
+
                for (j = 0; j < col_count; j++)
                        pg_wcsformat((unsigned char*)ptr[j], strlen(ptr[j]), encoding, col_lineptrs[j], heights[j]);
        
@@ -630,11 +660,11 @@ print_aligned_text(const char *title, const char *const * headers,
                }
        }
 
-       if (opt_border == 2)
+       if (opt_border == 2 && !cancel_pressed)
                _print_horizontal_line(col_count, widths, opt_border, fout);
 
        /* print footers */
-       if (footers && !opt_tuples_only)
+       if (footers && !opt_tuples_only && !cancel_pressed)
                for (ptr = footers; *ptr; ptr++)
                        fprintf(fout, "%s\n", *ptr);
 
@@ -681,6 +711,9 @@ print_aligned_vertical(const char *title, const char *const * headers,
        char       *divider;
        unsigned int cell_count = 0;
        struct lineptr *hlineptr, *dlineptr;
+
+       if (cancel_pressed)
+               return;
        
        if (cells[0] == NULL)
        {
@@ -764,6 +797,8 @@ print_aligned_vertical(const char *title, const char *const * headers,
                
                if (i % col_count == 0)
                {
+                       if (cancel_pressed)
+                               break;
                        if (!opt_tuples_only)
                        {
                                char       *record_str = pg_local_malloc(32);
@@ -860,12 +895,12 @@ print_aligned_vertical(const char *title, const char *const * headers,
                }
        }
 
-       if (opt_border == 2)
+       if (opt_border == 2 && !cancel_pressed)
                fprintf(fout, "%s\n", divider);
 
        /* print footers */
 
-       if (!opt_tuples_only && footers && *footers)
+       if (!opt_tuples_only && footers && *footers && !cancel_pressed)
        {
                if (opt_border < 2)
                        fputc('\n', fout);
@@ -943,6 +978,9 @@ print_html_text(const char *title, const char *const * headers,
        unsigned int i;
        const char *const * ptr;
 
+       if (cancel_pressed)
+               return;
+
        fprintf(fout, "<table border=\"%d\"", opt_border);
        if (opt_table_attr)
                fprintf(fout, " %s", opt_table_attr);
@@ -976,7 +1014,11 @@ print_html_text(const char *title, const char *const * headers,
        for (i = 0, ptr = cells; *ptr; i++, ptr++)
        {
                if (i % col_count == 0)
+               {
+                       if (cancel_pressed)
+                               break;
                        fputs("  <tr valign=\"top\">\n", fout);
+               }
 
                fprintf(fout, "    <td align=\"%s\">", opt_align[(i) % col_count] == 'r' ? "right" : "left");
                /* is string only whitespace? */
@@ -1002,7 +1044,7 @@ print_html_text(const char *title, const char *const * headers,
 
        /* print footers */
 
-       if (!opt_tuples_only && footers && *footers)
+       if (!opt_tuples_only && footers && *footers && !cancel_pressed)
        {
                fputs("<p>", fout);
                for (ptr = footers; *ptr; ptr++)
@@ -1029,6 +1071,9 @@ print_html_vertical(const char *title, const char *const * headers,
        unsigned int record = 1;
        const char *const * ptr;
 
+       if (cancel_pressed)
+               return;
+
        fprintf(fout, "<table border=\"%d\"", opt_border);
        if (opt_table_attr)
                fprintf(fout, " %s", opt_table_attr);
@@ -1051,6 +1096,8 @@ print_html_vertical(const char *title, const char *const * headers,
        {
                if (i % col_count == 0)
                {
+                       if (cancel_pressed)
+                               break;
                        if (!opt_tuples_only)
                                fprintf(fout, "\n  <tr><td colspan=\"2\" align=\"center\">Record %d</td></tr>\n", record++);
                        else
@@ -1081,7 +1128,7 @@ print_html_vertical(const char *title, const char *const * headers,
        fputs("</table>\n", fout);
 
        /* print footers */
-       if (!opt_tuples_only && footers && *footers)
+       if (!opt_tuples_only && footers && *footers && !cancel_pressed)
        {
                fputs("<p>", fout);
                for (ptr = footers; *ptr; ptr++)
@@ -1151,6 +1198,8 @@ print_latex_text(const char *title, const char *const * headers,
        unsigned int i;
        const char *const * ptr;
 
+       if (cancel_pressed)
+               return;
 
        /* print title */
        if (!opt_tuples_only && title)
@@ -1216,7 +1265,11 @@ print_latex_text(const char *title, const char *const * headers,
                        latex_escaped_print(*ptr, fout);
 
                if ((i + 1) % col_count == 0)
+               {
                        fputs(" \\\\\n", fout);
+                       if (cancel_pressed)
+                               break;
+               }
                else
                        fputs(" & ", fout);
        }
@@ -1229,7 +1282,7 @@ print_latex_text(const char *title, const char *const * headers,
 
        /* print footers */
 
-       if (footers && !opt_tuples_only)
+       if (footers && !opt_tuples_only && !cancel_pressed)
                for (ptr = footers; *ptr; ptr++)
                {
                        latex_escaped_print(*ptr, fout);
@@ -1255,6 +1308,9 @@ print_latex_vertical(const char *title, const char *const * headers,
 
        (void) opt_align;                       /* currently unused parameter */
 
+       if (cancel_pressed)
+               return;
+
        /* print title */
        if (!opt_tuples_only && title)
        {
@@ -1285,6 +1341,8 @@ print_latex_vertical(const char *title, const char *const * headers,
                /* new record */
                if (i % col_count == 0)
                {
+                       if (cancel_pressed)
+                               break;
                        if (!opt_tuples_only)
                        {
                                if (opt_border == 2)
@@ -1313,7 +1371,7 @@ print_latex_vertical(const char *title, const char *const * headers,
 
        /* print footers */
 
-       if (footers && !opt_tuples_only)
+       if (footers && !opt_tuples_only && !cancel_pressed)
                for (ptr = footers; *ptr; ptr++)
                {
                        if (opt_numeric_locale)
@@ -1367,6 +1425,8 @@ print_troff_ms_text(const char *title, const char *const * headers,
        unsigned int i;
        const char *const * ptr;
 
+       if (cancel_pressed)
+               return;
 
        /* print title */
        if (!opt_tuples_only && title)
@@ -1425,7 +1485,11 @@ print_troff_ms_text(const char *title, const char *const * headers,
                        troff_ms_escaped_print(*ptr, fout);
 
                if ((i + 1) % col_count == 0)
+               {
                        fputc('\n', fout);
+                       if (cancel_pressed)
+                               break;
+               }
                else
                        fputc('\t', fout);
        }
@@ -1435,7 +1499,7 @@ print_troff_ms_text(const char *title, const char *const * headers,
 
        /* print footers */
 
-       if (footers && !opt_tuples_only)
+       if (footers && !opt_tuples_only && !cancel_pressed)
                for (ptr = footers; *ptr; ptr++)
                {
                        troff_ms_escaped_print(*ptr, fout);
@@ -1462,6 +1526,9 @@ print_troff_ms_vertical(const char *title, const char *const * headers,
 
        (void) opt_align;                       /* currently unused parameter */
 
+       if (cancel_pressed)
+               return;
+
        /* print title */
        if (!opt_tuples_only && title)
        {
@@ -1491,6 +1558,8 @@ print_troff_ms_vertical(const char *title, const char *const * headers,
                /* new record */
                if (i % col_count == 0)
                {
+                       if (cancel_pressed)
+                               break;
                        if (!opt_tuples_only)
                        {
                                if (current_format != 1)
@@ -1542,7 +1611,7 @@ print_troff_ms_vertical(const char *title, const char *const * headers,
 
        /* print footers */
 
-       if (footers && !opt_tuples_only)
+       if (footers && !opt_tuples_only && !cancel_pressed)
                for (ptr = footers; *ptr; ptr++)
                {
                        troff_ms_escaped_print(*ptr, fout);
@@ -1618,6 +1687,9 @@ printTable(const char *title,
        FILE       *output;
        bool            use_expanded;
 
+       if (cancel_pressed)
+               return;
+
        if (opt->format == PRINT_NOTHING)
                return;
 
@@ -1731,6 +1803,17 @@ printTable(const char *title,
        /* Only close if we used the pager */
        if (fout == stdout && output != stdout)
        {
+               /*
+                * If printing was canceled midstream, warn about it.
+                *
+                * Some pagers like less use Ctrl-C as part of their command
+                * set. Even so, we abort our processing and warn the user
+                * what we did.  If the pager quit as a result of the
+                * SIGINT, this message won't go anywhere ...
+                */
+               if (cancel_pressed)
+                       fprintf(output, _("Interrupted\n"));
+
                pclose(output);
 #ifndef WIN32
                pqsignal(SIGPIPE, SIG_DFL);
@@ -1751,6 +1834,9 @@ printQuery(const PGresult *result, const printQueryOpt *opt, FILE *fout, FILE *f
        char       *align;
        int                     i;
 
+       if (cancel_pressed)
+               return;
+
        /* extract headers */
        nfields = PQnfields(result);
 
@@ -1798,18 +1884,24 @@ printQuery(const PGresult *result, const printQueryOpt *opt, FILE *fout, FILE *f
        {
                Oid                     ftype = PQftype(result, i);
 
-               if (ftype == 20 ||              /* int8 */
-                       ftype == 21 ||          /* int2 */
-                       ftype == 23 ||          /* int4 */
-                       (ftype >= 26 && ftype <= 30) ||         /* ?id */
-                       ftype == 700 ||         /* float4 */
-                       ftype == 701 ||         /* float8 */
-                       ftype == 790 ||         /* money */
-                       ftype == 1700           /* numeric */
-                       )
-                       align[i] = 'r';
-               else
-                       align[i] = 'l';
+               switch (ftype)
+               {
+                       case INT2OID:
+                       case INT4OID:
+                       case INT8OID:
+                       case FLOAT4OID:
+                       case FLOAT8OID:
+                       case NUMERICOID:
+                       case OIDOID:
+                       case XIDOID:
+                       case CIDOID:
+                       case CASHOID:
+                               align[i] = 'r';
+                               break;
+                       default:
+                               align[i] = 'l';
+                               break;
+               }
        }
 
        /* call table printer */
index 00e8cc83290bc6d12767bb3ff77ee711f5006aee..72bc63e4a7c61c4369908899086d2260476fda4b 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 2000-2006, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/bin/psql/startup.c,v 1.132 2006/04/27 02:58:08 momjian Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/startup.c,v 1.133 2006/06/14 16:49:02 tgl Exp $
  */
 #include "postgres_fe.h"
 
@@ -134,7 +134,6 @@ main(int argc, char *argv[])
 
 #ifdef WIN32
        setvbuf(stderr, NULL, _IONBF, 0);
-       setup_win32_locks();
 #endif
        setDecimalLocale();
        pset.cur_cmd_source = stdin;
@@ -371,6 +370,9 @@ main(int argc, char *argv[])
                if (options.action_string)              /* -f - was used */
                        pset.inputfile = "<stdin>";
 
+               /* establish control-C handling for interactive operation */
+               setup_cancel_handler();
+
                successResult = MainLoop(stdin);
        }
 
index da13cb78d71a19bc55e1c27862a2c5a4ba3d1a6d..26b9f8b925d699a43bc6edcc32c9040f9fcf8491 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 2000-2006, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/bin/psql/variables.c,v 1.23 2006/03/05 15:58:52 momjian Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/variables.c,v 1.24 2006/06/14 16:49:03 tgl Exp $
  */
 #include "postgres_fe.h"
 #include "common.h"
@@ -127,7 +127,11 @@ PrintVariables(VariableSpace space)
        struct _variable *ptr;
 
        for (ptr = space->next; ptr; ptr = ptr->next)
+       {
                printf("%s = '%s'\n", ptr->name, ptr->value);
+               if (cancel_pressed)
+                       break;
+       }
 }
 
 bool
index 1cd3baa5e1b00c5beb628fca8318cb320d7e40c5..eff3fa5ad1325c96895ca06a0fb46f333e0fd4f7 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/port/sprompt.c,v 1.16 2006/03/05 15:59:10 momjian Exp $
+ *       $PostgreSQL: pgsql/src/port/sprompt.c,v 1.17 2006/06/14 16:49:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -32,7 +32,6 @@
 #include <termios.h>
 #endif
 
-bool           prompt_state = false;
 extern char *simple_prompt(const char *prompt, int maxlen, bool echo);
 
 char *
@@ -57,8 +56,6 @@ simple_prompt(const char *prompt, int maxlen, bool echo)
        if (!destination)
                return NULL;
 
-       prompt_state = true;            /* disable SIGINT */
-
        /*
         * Do not try to collapse these into one "w+" mode file. Doesn't work on
         * some platforms (eg, HPUX 10.20).
@@ -159,7 +156,5 @@ simple_prompt(const char *prompt, int maxlen, bool echo)
                fclose(termout);
        }
 
-       prompt_state = false;           /* SIGINT okay again */
-
        return destination;
 }