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.
This commit is contained in:
Tom Lane 2006-06-14 16:49:03 +00:00
parent ace93353ea
commit f3164c0200
12 changed files with 352 additions and 161 deletions

View 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 :-(
*/
/* 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:

View 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 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);

View 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;
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;

View 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);

View 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;
}

View 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);

View 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"
@ -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);

View 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);

View 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>
@ -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 */

View 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);
}

View 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

View 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;
}