diff --git a/src/bin/psql/common.c b/src/bin/psql/common.c index 6b7f683b05..c35d3c3eef 100644 --- a/src/bin/psql/common.c +++ b/src/bin/psql/common.c @@ -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 #include /* for write() */ -#include #else #include /* for _write() */ #include @@ -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 :-( + */ - /* Perform query cancel */ + /* 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) { - 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: diff --git a/src/bin/psql/common.h b/src/bin/psql/common.h index 4cf5da0bf3..6045b7587d 100644 --- a/src/bin/psql/common.h +++ b/src/bin/psql/common.h @@ -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 -#include "pqsignal.h" +#include #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 sigint_interrupt_enabled; + +extern sigjmp_buf sigint_interrupt_jmp; + extern volatile bool cancel_pressed; +/* Note: cancel_pressed is defined in print.c, see that file for reasons */ -extern void ResetCancelConn(void); - -#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 abf4b60342..2ec01af186 100644 --- a/src/bin/psql/copy.c +++ b/src/bin/psql/copy.c @@ -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; - if (!fgets(buf, COPYBUFSIZ, copystream)) + /* enable longjmp while waiting for input */ + sigint_interrupt_enabled = true; + + fgresult = fgets(buf, COPYBUFSIZ, copystream); + + sigint_interrupt_enabled = false; + + if (!fgresult) { copydone = true; break; diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 9c9f609407..7053f5beff 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -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); diff --git a/src/bin/psql/input.c b/src/bin/psql/input.c index b970175b80..266986a886 100644 --- a/src/bin/psql/input.c +++ b/src/bin/psql/input.c @@ -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; } diff --git a/src/bin/psql/large_obj.c b/src/bin/psql/large_obj.c index 5de0348b39..9e8d6d2945 100644 --- a/src/bin/psql/large_obj.c +++ b/src/bin/psql/large_obj.c @@ -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); diff --git a/src/bin/psql/mainloop.c b/src/bin/psql/mainloop.c index af297ae2a9..b7022281e5 100644 --- a/src/bin/psql/mainloop.c +++ b/src/bin/psql/mainloop.c @@ -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" @@ -17,11 +17,6 @@ #include "psqlscan.h" #include "settings.h" -#ifndef WIN32 -#include -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 42ba65303f..eedb48f3e7 100644 --- a/src/bin/psql/mainloop.h +++ b/src/bin/psql/mainloop.h @@ -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 -#ifndef WIN32 -#include - -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 1ad04783d0..97f6f1e665 100644 --- a/src/bin/psql/print.c +++ b/src/bin/psql/print.c @@ -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 #include @@ -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, "\n", fout); + } fprintf(fout, "
", 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("

", 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, "\n", record++); else @@ -1081,7 +1128,7 @@ print_html_vertical(const char *title, const char *const * headers, fputs("
Record %d
\n", fout); /* print footers */ - if (!opt_tuples_only && footers && *footers) + if (!opt_tuples_only && footers && *footers && !cancel_pressed) { fputs("

", 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 00e8cc8329..72bc63e4a7 100644 --- a/src/bin/psql/startup.c +++ b/src/bin/psql/startup.c @@ -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 = ""; + /* 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 da13cb78d7..26b9f8b925 100644 --- a/src/bin/psql/variables.c +++ b/src/bin/psql/variables.c @@ -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 diff --git a/src/port/sprompt.c b/src/port/sprompt.c index 1cd3baa5e1..eff3fa5ad1 100644 --- a/src/port/sprompt.c +++ b/src/port/sprompt.c @@ -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 #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; }