diff options
author | Tom Lane | 2006-06-14 16:49:03 +0000 |
---|---|---|
committer | Tom Lane | 2006-06-14 16:49:03 +0000 |
commit | e555fc7adf14276ad16f22e4551821a10946d38d (patch) | |
tree | ea7c87e90460f520723b66dd028d5f207592f7fd | |
parent | 1c61a64901ca5b887afc158cb9a5de9ff8cad3df (diff) |
Clean up psql's control-C handling to avoid longjmp'ing out of random
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.
-rw-r--r-- | src/bin/psql/common.c | 126 | ||||
-rw-r--r-- | src/bin/psql/common.h | 18 | ||||
-rw-r--r-- | src/bin/psql/copy.c | 57 | ||||
-rw-r--r-- | src/bin/psql/describe.c | 5 | ||||
-rw-r--r-- | src/bin/psql/input.c | 62 | ||||
-rw-r--r-- | src/bin/psql/large_obj.c | 13 | ||||
-rw-r--r-- | src/bin/psql/mainloop.c | 44 | ||||
-rw-r--r-- | src/bin/psql/mainloop.h | 5 | ||||
-rw-r--r-- | src/bin/psql/print.c | 146 | ||||
-rw-r--r-- | src/bin/psql/startup.c | 4 | ||||
-rw-r--r-- | src/bin/psql/variables.c | 4 | ||||
-rw-r--r-- | src/port/sprompt.c | 5 |
12 files changed, 340 insertions, 149 deletions
diff --git a/src/bin/psql/common.c b/src/bin/psql/common.c index 27acf6d01a..439d90ce47 100644 --- a/src/bin/psql/common.c +++ b/src/bin/psql/common.c @@ -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 @@ -305,23 +328,13 @@ consoleHandler(DWORD dwCtrlType) } 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: diff --git a/src/bin/psql/common.h b/src/bin/psql/common.h index 547ee6227c..322a5ab5eb 100644 --- a/src/bin/psql/common.h +++ b/src/bin/psql/common.h @@ -9,8 +9,7 @@ #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); diff --git a/src/bin/psql/copy.c b/src/bin/psql/copy.c index 35b8f0decc..9914f30bd7 100644 --- a/src/bin/psql/copy.c +++ b/src/bin/psql/copy.c @@ -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; diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index bf200293b8..c2791852b4 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -663,6 +663,11 @@ describeTableDetails(const char *pattern, bool verbose) PQclear(res); return false; } + if (cancel_pressed) + { + PQclear(res); + return false; + } } PQclear(res); diff --git a/src/bin/psql/input.c b/src/bin/psql/input.c index e6eb9a8065..5462167b7a 100644 --- a/src/bin/psql/input.c +++ b/src/bin/psql/input.c @@ -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; } diff --git a/src/bin/psql/large_obj.c b/src/bin/psql/large_obj.c index 7215638f82..326e669891 100644 --- a/src/bin/psql/large_obj.c +++ b/src/bin/psql/large_obj.c @@ -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); diff --git a/src/bin/psql/mainloop.c b/src/bin/psql/mainloop.c index ef059ec969..e0db2eb33e 100644 --- a/src/bin/psql/mainloop.c +++ b/src/bin/psql/mainloop.c @@ -17,11 +17,6 @@ #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); diff --git a/src/bin/psql/mainloop.h b/src/bin/psql/mainloop.h index 550392e11f..45dd656de8 100644 --- a/src/bin/psql/mainloop.h +++ b/src/bin/psql/mainloop.h @@ -10,11 +10,6 @@ #include "postgres_fe.h" #include <stdio.h> -#ifndef WIN32 -#include <setjmp.h> - -extern sigjmp_buf main_loop_jmp; -#endif int MainLoop(FILE *source); diff --git a/src/bin/psql/print.c b/src/bin/psql/print.c index fba24430b8..79221559d6 100644 --- a/src/bin/psql/print.c +++ b/src/bin/psql/print.c @@ -4,10 +4,14 @@ * Copyright (c) 2000-2006, PostgreSQL Global Development Group * * $PostgreSQL$ + * + * 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> @@ -28,6 +32,17 @@ #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 */ diff --git a/src/bin/psql/startup.c b/src/bin/psql/startup.c index 6b7c3bfa83..2ebc3a8684 100644 --- a/src/bin/psql/startup.c +++ b/src/bin/psql/startup.c @@ -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); } diff --git a/src/bin/psql/variables.c b/src/bin/psql/variables.c index 7c57060abc..1a54a95190 100644 --- a/src/bin/psql/variables.c +++ b/src/bin/psql/variables.c @@ -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 diff --git a/src/port/sprompt.c b/src/port/sprompt.c index 0a458379ad..9c14106ab1 100644 --- a/src/port/sprompt.c +++ b/src/port/sprompt.c @@ -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; } |