mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-11-21 03:13:05 +08:00
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:
parent
ace93353ea
commit
f3164c0200
@ -3,7 +3,7 @@
|
|||||||
*
|
*
|
||||||
* Copyright (c) 2000-2006, PostgreSQL Global Development Group
|
* 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 "postgres_fe.h"
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
@ -16,7 +16,6 @@
|
|||||||
#ifndef WIN32
|
#ifndef WIN32
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include <unistd.h> /* for write() */
|
#include <unistd.h> /* for write() */
|
||||||
#include <setjmp.h>
|
|
||||||
#else
|
#else
|
||||||
#include <io.h> /* for _write() */
|
#include <io.h> /* for _write() */
|
||||||
#include <win32.h>
|
#include <win32.h>
|
||||||
@ -58,8 +57,6 @@ typedef struct _timeb TimevalStruct;
|
|||||||
((T)->millitm - (U)->millitm))
|
((T)->millitm - (U)->millitm))
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
extern bool prompt_state;
|
|
||||||
|
|
||||||
|
|
||||||
static bool command_no_begin(const char *query);
|
static bool command_no_begin(const char *query);
|
||||||
|
|
||||||
@ -219,46 +216,61 @@ NoticeProcessor(void *arg, const char *message)
|
|||||||
/*
|
/*
|
||||||
* Code to support query cancellation
|
* 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
|
* cancel request to the backend. Note that sending the cancel directly from
|
||||||
* the signal handler is safe because PQcancel() is written to make it
|
* 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.
|
* facilities in a signal handler.
|
||||||
*
|
*
|
||||||
* On win32, the signal cancelling happens on a separate thread, because
|
* On win32, the signal cancelling happens on a separate thread, because
|
||||||
* that's how SetConsoleCtrlHandler works. The PQcancel function is safe
|
* that's how SetConsoleCtrlHandler works. The PQcancel function is safe
|
||||||
* for this (unlike PQrequestCancel). However, a CRITICAL_SECTION is required
|
* 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.
|
* 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
|
#ifdef WIN32
|
||||||
static CRITICAL_SECTION cancelConnLock;
|
static CRITICAL_SECTION cancelConnLock;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
volatile bool cancel_pressed = false;
|
|
||||||
|
|
||||||
#define write_stderr(str) write(fileno(stderr), str, strlen(str))
|
#define write_stderr(str) write(fileno(stderr), str, strlen(str))
|
||||||
|
|
||||||
|
|
||||||
#ifndef WIN32
|
#ifndef WIN32
|
||||||
|
|
||||||
void
|
static void
|
||||||
handle_sigint(SIGNAL_ARGS)
|
handle_sigint(SIGNAL_ARGS)
|
||||||
{
|
{
|
||||||
int save_errno = errno;
|
int save_errno = errno;
|
||||||
char errbuf[256];
|
char errbuf[256];
|
||||||
|
|
||||||
/* Don't muck around if prompting for a password. */
|
/* if we are waiting for input, longjmp out of it */
|
||||||
if (prompt_state)
|
if (sigint_interrupt_enabled)
|
||||||
return;
|
{
|
||||||
|
sigint_interrupt_enabled = false;
|
||||||
if (cancelConn == NULL)
|
siglongjmp(sigint_interrupt_jmp, 1);
|
||||||
siglongjmp(main_loop_jmp, 1);
|
}
|
||||||
|
|
||||||
|
/* else, set cancel flag to stop any long-running loops */
|
||||||
cancel_pressed = true;
|
cancel_pressed = true;
|
||||||
|
|
||||||
|
/* and send QueryCancel if we are processing a database query */
|
||||||
|
if (cancelConn != NULL)
|
||||||
|
{
|
||||||
if (PQcancel(cancelConn, errbuf, sizeof(errbuf)))
|
if (PQcancel(cancelConn, errbuf, sizeof(errbuf)))
|
||||||
write_stderr("Cancel request sent\n");
|
write_stderr("Cancel request sent\n");
|
||||||
else
|
else
|
||||||
@ -266,8 +278,17 @@ handle_sigint(SIGNAL_ARGS)
|
|||||||
write_stderr("Could not send cancel request: ");
|
write_stderr("Could not send cancel request: ");
|
||||||
write_stderr(errbuf);
|
write_stderr(errbuf);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
errno = save_errno; /* just in case the write changed it */
|
errno = save_errno; /* just in case the write changed it */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
setup_cancel_handler(void)
|
||||||
|
{
|
||||||
|
pqsignal(SIGINT, handle_sigint);
|
||||||
|
}
|
||||||
|
|
||||||
#else /* WIN32 */
|
#else /* WIN32 */
|
||||||
|
|
||||||
static BOOL WINAPI
|
static BOOL WINAPI
|
||||||
@ -278,15 +299,17 @@ consoleHandler(DWORD dwCtrlType)
|
|||||||
if (dwCtrlType == CTRL_C_EVENT ||
|
if (dwCtrlType == CTRL_C_EVENT ||
|
||||||
dwCtrlType == CTRL_BREAK_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);
|
EnterCriticalSection(&cancelConnLock);
|
||||||
if (cancelConn != NULL)
|
if (cancelConn != NULL)
|
||||||
{
|
{
|
||||||
cancel_pressed = true;
|
|
||||||
|
|
||||||
if (PQcancel(cancelConn, errbuf, sizeof(errbuf)))
|
if (PQcancel(cancelConn, errbuf, sizeof(errbuf)))
|
||||||
write_stderr("Cancel request sent\n");
|
write_stderr("Cancel request sent\n");
|
||||||
else
|
else
|
||||||
@ -304,24 +327,14 @@ consoleHandler(DWORD dwCtrlType)
|
|||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
setup_win32_locks(void)
|
|
||||||
{
|
|
||||||
InitializeCriticalSection(&cancelConnLock);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
setup_cancel_handler(void)
|
setup_cancel_handler(void)
|
||||||
{
|
{
|
||||||
static bool done = false;
|
InitializeCriticalSection(&cancelConnLock);
|
||||||
|
|
||||||
/* only need one handler per process */
|
|
||||||
if (!done)
|
|
||||||
{
|
|
||||||
SetConsoleCtrlHandler(consoleHandler, TRUE);
|
SetConsoleCtrlHandler(consoleHandler, TRUE);
|
||||||
done = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* WIN32 */
|
#endif /* WIN32 */
|
||||||
|
|
||||||
|
|
||||||
@ -386,16 +399,22 @@ CheckConnection(void)
|
|||||||
*
|
*
|
||||||
* Set cancelConn to point to the current database connection.
|
* Set cancelConn to point to the current database connection.
|
||||||
*/
|
*/
|
||||||
static void
|
void
|
||||||
SetCancelConn(void)
|
SetCancelConn(void)
|
||||||
{
|
{
|
||||||
|
PGcancel *oldCancelConn;
|
||||||
|
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
EnterCriticalSection(&cancelConnLock);
|
EnterCriticalSection(&cancelConnLock);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Free the old one if we have one */
|
/* Free the old one if we have one */
|
||||||
if (cancelConn != NULL)
|
oldCancelConn = cancelConn;
|
||||||
PQfreeCancel(cancelConn);
|
/* be sure handle_sigint doesn't use pointer while freeing */
|
||||||
|
cancelConn = NULL;
|
||||||
|
|
||||||
|
if (oldCancelConn != NULL)
|
||||||
|
PQfreeCancel(oldCancelConn);
|
||||||
|
|
||||||
cancelConn = PQgetCancel(pset.db);
|
cancelConn = PQgetCancel(pset.db);
|
||||||
|
|
||||||
@ -413,15 +432,19 @@ SetCancelConn(void)
|
|||||||
void
|
void
|
||||||
ResetCancelConn(void)
|
ResetCancelConn(void)
|
||||||
{
|
{
|
||||||
|
PGcancel *oldCancelConn;
|
||||||
|
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
EnterCriticalSection(&cancelConnLock);
|
EnterCriticalSection(&cancelConnLock);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (cancelConn)
|
oldCancelConn = cancelConn;
|
||||||
PQfreeCancel(cancelConn);
|
/* be sure handle_sigint doesn't use pointer while freeing */
|
||||||
|
|
||||||
cancelConn = NULL;
|
cancelConn = NULL;
|
||||||
|
|
||||||
|
if (oldCancelConn != NULL)
|
||||||
|
PQfreeCancel(oldCancelConn);
|
||||||
|
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
LeaveCriticalSection(&cancelConnLock);
|
LeaveCriticalSection(&cancelConnLock);
|
||||||
#endif
|
#endif
|
||||||
@ -453,15 +476,8 @@ AcceptResult(const PGresult *result, const char *query)
|
|||||||
case PGRES_TUPLES_OK:
|
case PGRES_TUPLES_OK:
|
||||||
case PGRES_EMPTY_QUERY:
|
case PGRES_EMPTY_QUERY:
|
||||||
case PGRES_COPY_IN:
|
case PGRES_COPY_IN:
|
||||||
/* Fine, do nothing */
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PGRES_COPY_OUT:
|
case PGRES_COPY_OUT:
|
||||||
/*
|
/* Fine, do nothing */
|
||||||
* Keep cancel connection active during copy out state.
|
|
||||||
* The matching ResetCancelConn() is in handleCopyOut.
|
|
||||||
*/
|
|
||||||
SetCancelConn();
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -648,12 +664,16 @@ ProcessCopyResult(PGresult *results)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case PGRES_COPY_OUT:
|
case PGRES_COPY_OUT:
|
||||||
|
SetCancelConn();
|
||||||
success = handleCopyOut(pset.db, pset.queryFout);
|
success = handleCopyOut(pset.db, pset.queryFout);
|
||||||
|
ResetCancelConn();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PGRES_COPY_IN:
|
case PGRES_COPY_IN:
|
||||||
|
SetCancelConn();
|
||||||
success = handleCopyIn(pset.db, pset.cur_cmd_source,
|
success = handleCopyIn(pset.db, pset.cur_cmd_source,
|
||||||
PQbinaryTuples(results));
|
PQbinaryTuples(results));
|
||||||
|
ResetCancelConn();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -3,14 +3,13 @@
|
|||||||
*
|
*
|
||||||
* Copyright (c) 2000-2006, PostgreSQL Global Development Group
|
* 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
|
#ifndef COMMON_H
|
||||||
#define COMMON_H
|
#define COMMON_H
|
||||||
|
|
||||||
#include "postgres_fe.h"
|
#include "postgres_fe.h"
|
||||||
#include <signal.h>
|
#include <setjmp.h>
|
||||||
#include "pqsignal.h"
|
|
||||||
#include "libpq-fe.h"
|
#include "libpq-fe.h"
|
||||||
|
|
||||||
#ifdef USE_ASSERT_CHECKING
|
#ifdef USE_ASSERT_CHECKING
|
||||||
@ -41,16 +40,17 @@ __attribute__((format(printf, 1, 2)));
|
|||||||
|
|
||||||
extern void NoticeProcessor(void *arg, const char *message);
|
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;
|
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);
|
extern void setup_cancel_handler(void);
|
||||||
#endif
|
|
||||||
|
extern void SetCancelConn(void);
|
||||||
|
extern void ResetCancelConn(void);
|
||||||
|
|
||||||
extern PGresult *PSQLexec(const char *query, bool start_xact);
|
extern PGresult *PSQLexec(const char *query, bool start_xact);
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
*
|
*
|
||||||
* Copyright (c) 2000-2006, PostgreSQL Global Development Group
|
* 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 "postgres_fe.h"
|
||||||
#include "copy.h"
|
#include "copy.h"
|
||||||
@ -550,11 +550,15 @@ do_copy(const char *args)
|
|||||||
switch (PQresultStatus(result))
|
switch (PQresultStatus(result))
|
||||||
{
|
{
|
||||||
case PGRES_COPY_OUT:
|
case PGRES_COPY_OUT:
|
||||||
|
SetCancelConn();
|
||||||
success = handleCopyOut(pset.db, copystream);
|
success = handleCopyOut(pset.db, copystream);
|
||||||
|
ResetCancelConn();
|
||||||
break;
|
break;
|
||||||
case PGRES_COPY_IN:
|
case PGRES_COPY_IN:
|
||||||
|
SetCancelConn();
|
||||||
success = handleCopyIn(pset.db, copystream,
|
success = handleCopyIn(pset.db, copystream,
|
||||||
PQbinaryTuples(result));
|
PQbinaryTuples(result));
|
||||||
|
ResetCancelConn();
|
||||||
break;
|
break;
|
||||||
case PGRES_NONFATAL_ERROR:
|
case PGRES_NONFATAL_ERROR:
|
||||||
case PGRES_FATAL_ERROR:
|
case PGRES_FATAL_ERROR:
|
||||||
@ -651,9 +655,6 @@ handleCopyOut(PGconn *conn, FILE *copystream)
|
|||||||
}
|
}
|
||||||
PQclear(res);
|
PQclear(res);
|
||||||
|
|
||||||
/* Disable cancel connection (see AcceptResult in common.c) */
|
|
||||||
ResetCancelConn();
|
|
||||||
|
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -675,11 +676,31 @@ handleCopyOut(PGconn *conn, FILE *copystream)
|
|||||||
bool
|
bool
|
||||||
handleCopyIn(PGconn *conn, FILE *copystream, bool isbinary)
|
handleCopyIn(PGconn *conn, FILE *copystream, bool isbinary)
|
||||||
{
|
{
|
||||||
bool OK = true;
|
bool OK;
|
||||||
const char *prompt;
|
const char *prompt;
|
||||||
char buf[COPYBUFSIZ];
|
char buf[COPYBUFSIZ];
|
||||||
PGresult *res;
|
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 */
|
/* Prompt if interactive input */
|
||||||
if (isatty(fileno(copystream)))
|
if (isatty(fileno(copystream)))
|
||||||
{
|
{
|
||||||
@ -691,10 +712,10 @@ handleCopyIn(PGconn *conn, FILE *copystream, bool isbinary)
|
|||||||
else
|
else
|
||||||
prompt = NULL;
|
prompt = NULL;
|
||||||
|
|
||||||
|
OK = true;
|
||||||
|
|
||||||
if (isbinary)
|
if (isbinary)
|
||||||
{
|
{
|
||||||
int buflen;
|
|
||||||
|
|
||||||
/* interactive input probably silly, but give one prompt anyway */
|
/* interactive input probably silly, but give one prompt anyway */
|
||||||
if (prompt)
|
if (prompt)
|
||||||
{
|
{
|
||||||
@ -702,8 +723,20 @@ handleCopyIn(PGconn *conn, FILE *copystream, bool isbinary)
|
|||||||
fflush(stdout);
|
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)
|
if (PQputCopyData(conn, buf, buflen) <= 0)
|
||||||
{
|
{
|
||||||
OK = false;
|
OK = false;
|
||||||
@ -732,8 +765,16 @@ handleCopyIn(PGconn *conn, FILE *copystream, bool isbinary)
|
|||||||
while (!linedone)
|
while (!linedone)
|
||||||
{ /* for each bufferload in line ... */
|
{ /* for each bufferload in line ... */
|
||||||
int linelen;
|
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;
|
copydone = true;
|
||||||
break;
|
break;
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
*
|
*
|
||||||
* Copyright (c) 2000-2006, PostgreSQL Global Development Group
|
* 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 "postgres_fe.h"
|
||||||
#include "describe.h"
|
#include "describe.h"
|
||||||
@ -663,6 +663,11 @@ describeTableDetails(const char *pattern, bool verbose)
|
|||||||
PQclear(res);
|
PQclear(res);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (cancel_pressed)
|
||||||
|
{
|
||||||
|
PQclear(res);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PQclear(res);
|
PQclear(res);
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
*
|
*
|
||||||
* Copyright (c) 2000-2006, PostgreSQL Global Development Group
|
* 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"
|
#include "postgres_fe.h"
|
||||||
|
|
||||||
@ -82,7 +82,9 @@ GetHistControlConfig(void)
|
|||||||
* gets_interactive()
|
* gets_interactive()
|
||||||
*
|
*
|
||||||
* Gets a line of interactive input, using readline if desired.
|
* 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 *
|
char *
|
||||||
gets_interactive(const char *prompt)
|
gets_interactive(const char *prompt)
|
||||||
@ -90,8 +92,18 @@ gets_interactive(const char *prompt)
|
|||||||
#ifdef USE_READLINE
|
#ifdef USE_READLINE
|
||||||
if (useReadline)
|
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 *) */
|
/* 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
|
#endif
|
||||||
|
|
||||||
@ -169,30 +181,56 @@ pg_send_history(PQExpBuffer history_buf)
|
|||||||
* gets_fromFile
|
* gets_fromFile
|
||||||
*
|
*
|
||||||
* Gets a line of noninteractive input from a file (which could be stdin).
|
* 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 *
|
char *
|
||||||
gets_fromFile(FILE *source)
|
gets_fromFile(FILE *source)
|
||||||
{
|
{
|
||||||
PQExpBufferData buffer;
|
static PQExpBuffer buffer = NULL;
|
||||||
|
|
||||||
char line[1024];
|
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);
|
char *result;
|
||||||
if (buffer.data[buffer.len - 1] == '\n')
|
|
||||||
|
/* 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';
|
buffer->data[buffer->len - 1] = '\0';
|
||||||
return buffer.data;
|
return pg_strdup(buffer->data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (buffer.len > 0)
|
if (buffer->len > 0) /* EOF after reading some bufferload(s) */
|
||||||
return buffer.data; /* EOF after reading some bufferload(s) */
|
return pg_strdup(buffer->data);
|
||||||
|
|
||||||
/* EOF, so return null */
|
/* EOF, so return null */
|
||||||
termPQExpBuffer(&buffer);
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
*
|
*
|
||||||
* Copyright (c) 2000-2006, PostgreSQL Global Development Group
|
* 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 "postgres_fe.h"
|
||||||
#include "large_obj.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))
|
if (!start_lo_xact("\\lo_export", &own_transaction))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
SetCancelConn();
|
||||||
status = lo_export(pset.db, atooid(loid_arg), filename_arg);
|
status = lo_export(pset.db, atooid(loid_arg), filename_arg);
|
||||||
|
ResetCancelConn();
|
||||||
|
|
||||||
|
/* of course this status is documented nowhere :( */
|
||||||
if (status != 1)
|
if (status != 1)
|
||||||
{ /* of course this status is documented nowhere
|
{
|
||||||
* :( */
|
|
||||||
fputs(PQerrorMessage(pset.db), stderr);
|
fputs(PQerrorMessage(pset.db), stderr);
|
||||||
return fail_lo_xact("\\lo_export", own_transaction);
|
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))
|
if (!start_lo_xact("\\lo_import", &own_transaction))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
SetCancelConn();
|
||||||
loid = lo_import(pset.db, filename_arg);
|
loid = lo_import(pset.db, filename_arg);
|
||||||
|
ResetCancelConn();
|
||||||
|
|
||||||
if (loid == InvalidOid)
|
if (loid == InvalidOid)
|
||||||
{
|
{
|
||||||
fputs(PQerrorMessage(pset.db), stderr);
|
fputs(PQerrorMessage(pset.db), stderr);
|
||||||
@ -211,7 +217,10 @@ do_lo_unlink(const char *loid_arg)
|
|||||||
if (!start_lo_xact("\\lo_unlink", &own_transaction))
|
if (!start_lo_xact("\\lo_unlink", &own_transaction))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
SetCancelConn();
|
||||||
status = lo_unlink(pset.db, loid);
|
status = lo_unlink(pset.db, loid);
|
||||||
|
ResetCancelConn();
|
||||||
|
|
||||||
if (status == -1)
|
if (status == -1)
|
||||||
{
|
{
|
||||||
fputs(PQerrorMessage(pset.db), stderr);
|
fputs(PQerrorMessage(pset.db), stderr);
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
*
|
*
|
||||||
* Copyright (c) 2000-2006, PostgreSQL Global Development Group
|
* 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 "postgres_fe.h"
|
||||||
#include "mainloop.h"
|
#include "mainloop.h"
|
||||||
@ -17,11 +17,6 @@
|
|||||||
#include "psqlscan.h"
|
#include "psqlscan.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
|
|
||||||
#ifndef WIN32
|
|
||||||
#include <setjmp.h>
|
|
||||||
sigjmp_buf main_loop_jmp;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Main processing loop for reading lines of input
|
* Main processing loop for reading lines of input
|
||||||
@ -80,16 +75,14 @@ MainLoop(FILE *source)
|
|||||||
while (successResult == EXIT_SUCCESS)
|
while (successResult == EXIT_SUCCESS)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* Welcome code for Control-C
|
* Clean up after a previous Control-C
|
||||||
*/
|
*/
|
||||||
if (cancel_pressed)
|
if (cancel_pressed)
|
||||||
{
|
{
|
||||||
if (!pset.cur_cmd_interactive)
|
if (!pset.cur_cmd_interactive)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* You get here if you stopped a script with Ctrl-C and a
|
* You get here if you stopped a script with Ctrl-C.
|
||||||
* query cancel was issued. In that case we don't do the
|
|
||||||
* longjmp, so the query routine can finish nicely.
|
|
||||||
*/
|
*/
|
||||||
successResult = EXIT_USER;
|
successResult = EXIT_USER;
|
||||||
break;
|
break;
|
||||||
@ -98,18 +91,24 @@ MainLoop(FILE *source)
|
|||||||
cancel_pressed = false;
|
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 */
|
/* got here with longjmp */
|
||||||
|
|
||||||
/* reset parsing state */
|
/* reset parsing state */
|
||||||
resetPQExpBuffer(query_buf);
|
|
||||||
psql_scan_finish(scan_state);
|
psql_scan_finish(scan_state);
|
||||||
psql_scan_reset(scan_state);
|
psql_scan_reset(scan_state);
|
||||||
|
resetPQExpBuffer(query_buf);
|
||||||
|
resetPQExpBuffer(history_buf);
|
||||||
count_eof = 0;
|
count_eof = 0;
|
||||||
slashCmdStatus = PSQL_CMD_UNKNOWN;
|
slashCmdStatus = PSQL_CMD_UNKNOWN;
|
||||||
prompt_status = PROMPT_READY;
|
prompt_status = PROMPT_READY;
|
||||||
|
cancel_pressed = false;
|
||||||
|
|
||||||
if (pset.cur_cmd_interactive)
|
if (pset.cur_cmd_interactive)
|
||||||
putc('\n', stdout);
|
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);
|
fflush(stdout);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -360,14 +351,13 @@ MainLoop(FILE *source)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Reset SIGINT handler because main_loop_jmp will be invalid as soon as
|
* Let's just make real sure the SIGINT handler won't try to use
|
||||||
* we exit this routine. If there is an outer MainLoop instance, it will
|
* sigint_interrupt_jmp after we exit this routine. If there is an outer
|
||||||
* re-enable ^C catching as soon as it gets back to the top of its loop
|
* MainLoop instance, it will reset sigint_interrupt_jmp to point to
|
||||||
* and resets main_loop_jmp to point to itself.
|
* itself at the top of its loop, before any further interactive input
|
||||||
|
* happens.
|
||||||
*/
|
*/
|
||||||
#ifndef WIN32
|
sigint_interrupt_enabled = false;
|
||||||
pqsignal(SIGINT, SIG_DFL);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
destroyPQExpBuffer(query_buf);
|
destroyPQExpBuffer(query_buf);
|
||||||
destroyPQExpBuffer(previous_buf);
|
destroyPQExpBuffer(previous_buf);
|
||||||
|
@ -3,18 +3,13 @@
|
|||||||
*
|
*
|
||||||
* Copyright (c) 2000-2006, PostgreSQL Global Development Group
|
* 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
|
#ifndef MAINLOOP_H
|
||||||
#define MAINLOOP_H
|
#define MAINLOOP_H
|
||||||
|
|
||||||
#include "postgres_fe.h"
|
#include "postgres_fe.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#ifndef WIN32
|
|
||||||
#include <setjmp.h>
|
|
||||||
|
|
||||||
extern sigjmp_buf main_loop_jmp;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
int MainLoop(FILE *source);
|
int MainLoop(FILE *source);
|
||||||
|
|
||||||
|
@ -3,11 +3,15 @@
|
|||||||
*
|
*
|
||||||
* Copyright (c) 2000-2006, PostgreSQL Global Development Group
|
* 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 "common.h"
|
||||||
#include "print.h"
|
#include "print.h"
|
||||||
|
#include "catalog/pg_type.h"
|
||||||
|
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
@ -28,6 +32,17 @@
|
|||||||
|
|
||||||
#include "mbprint.h"
|
#include "mbprint.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We define the cancel_pressed flag in this file, rather than common.c where
|
||||||
|
* it naturally belongs, because this file is also used by non-psql programs
|
||||||
|
* (see the bin/scripts/ directory). In those programs cancel_pressed will
|
||||||
|
* never become set and will have no effect.
|
||||||
|
*
|
||||||
|
* Note: print.c's general strategy for when to check cancel_pressed is to do
|
||||||
|
* so at completion of each row of output.
|
||||||
|
*/
|
||||||
|
volatile bool cancel_pressed = false;
|
||||||
|
|
||||||
static char *decimal_point;
|
static char *decimal_point;
|
||||||
static char *grouping;
|
static char *grouping;
|
||||||
static char *thousands_sep;
|
static char *thousands_sep;
|
||||||
@ -171,6 +186,9 @@ print_unaligned_text(const char *title, const char *const * headers,
|
|||||||
const char *const * ptr;
|
const char *const * ptr;
|
||||||
bool need_recordsep = false;
|
bool need_recordsep = false;
|
||||||
|
|
||||||
|
if (cancel_pressed)
|
||||||
|
return;
|
||||||
|
|
||||||
if (!opt_fieldsep)
|
if (!opt_fieldsep)
|
||||||
opt_fieldsep = "";
|
opt_fieldsep = "";
|
||||||
if (!opt_recordsep)
|
if (!opt_recordsep)
|
||||||
@ -202,6 +220,8 @@ print_unaligned_text(const char *title, const char *const * headers,
|
|||||||
{
|
{
|
||||||
fputs(opt_recordsep, fout);
|
fputs(opt_recordsep, fout);
|
||||||
need_recordsep = false;
|
need_recordsep = false;
|
||||||
|
if (cancel_pressed)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if (opt_align[i % col_count] == 'r' && opt_numeric_locale)
|
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 */
|
/* print footers */
|
||||||
|
|
||||||
if (!opt_tuples_only && footers)
|
if (!opt_tuples_only && footers && !cancel_pressed)
|
||||||
for (ptr = footers; *ptr; ptr++)
|
for (ptr = footers; *ptr; ptr++)
|
||||||
{
|
{
|
||||||
if (need_recordsep)
|
if (need_recordsep)
|
||||||
@ -252,6 +272,9 @@ print_unaligned_vertical(const char *title, const char *const * headers,
|
|||||||
unsigned int i;
|
unsigned int i;
|
||||||
const char *const * ptr;
|
const char *const * ptr;
|
||||||
|
|
||||||
|
if (cancel_pressed)
|
||||||
|
return;
|
||||||
|
|
||||||
if (!opt_fieldsep)
|
if (!opt_fieldsep)
|
||||||
opt_fieldsep = "";
|
opt_fieldsep = "";
|
||||||
if (!opt_recordsep)
|
if (!opt_recordsep)
|
||||||
@ -273,6 +296,8 @@ print_unaligned_vertical(const char *title, const char *const * headers,
|
|||||||
fputs(opt_recordsep, fout);
|
fputs(opt_recordsep, fout);
|
||||||
if (i % col_count == 0)
|
if (i % col_count == 0)
|
||||||
fputs(opt_recordsep, fout); /* another one */
|
fputs(opt_recordsep, fout); /* another one */
|
||||||
|
if (cancel_pressed)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
fputs(headers[i % col_count], fout);
|
fputs(headers[i % col_count], fout);
|
||||||
@ -289,7 +314,7 @@ print_unaligned_vertical(const char *title, const char *const * headers,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* print footers */
|
/* print footers */
|
||||||
if (!opt_tuples_only && footers && *footers)
|
if (!opt_tuples_only && footers && *footers && !cancel_pressed)
|
||||||
{
|
{
|
||||||
fputs(opt_recordsep, fout);
|
fputs(opt_recordsep, fout);
|
||||||
for (ptr = footers; *ptr; ptr++)
|
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 */
|
int *complete; /* Array remembering which columns have completed output */
|
||||||
|
|
||||||
|
if (cancel_pressed)
|
||||||
|
return;
|
||||||
|
|
||||||
/* count columns */
|
/* count columns */
|
||||||
for (ptr = headers; *ptr; ptr++)
|
for (ptr = headers; *ptr; ptr++)
|
||||||
col_count++;
|
col_count++;
|
||||||
@ -543,7 +571,6 @@ print_aligned_text(const char *title, const char *const * headers,
|
|||||||
fputc('\n', fout);
|
fputc('\n', fout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
_print_horizontal_line(col_count, widths, opt_border, fout);
|
_print_horizontal_line(col_count, widths, opt_border, fout);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -554,6 +581,9 @@ print_aligned_text(const char *title, const char *const * headers,
|
|||||||
int cols_todo = col_count;
|
int cols_todo = col_count;
|
||||||
int line_count; /* Number of lines output so far in row */
|
int line_count; /* Number of lines output so far in row */
|
||||||
|
|
||||||
|
if (cancel_pressed)
|
||||||
|
break;
|
||||||
|
|
||||||
for (j = 0; j < col_count; j++)
|
for (j = 0; j < col_count; j++)
|
||||||
pg_wcsformat((unsigned char*)ptr[j], strlen(ptr[j]), encoding, col_lineptrs[j], heights[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_horizontal_line(col_count, widths, opt_border, fout);
|
||||||
|
|
||||||
/* print footers */
|
/* print footers */
|
||||||
if (footers && !opt_tuples_only)
|
if (footers && !opt_tuples_only && !cancel_pressed)
|
||||||
for (ptr = footers; *ptr; ptr++)
|
for (ptr = footers; *ptr; ptr++)
|
||||||
fprintf(fout, "%s\n", *ptr);
|
fprintf(fout, "%s\n", *ptr);
|
||||||
|
|
||||||
@ -682,6 +712,9 @@ print_aligned_vertical(const char *title, const char *const * headers,
|
|||||||
unsigned int cell_count = 0;
|
unsigned int cell_count = 0;
|
||||||
struct lineptr *hlineptr, *dlineptr;
|
struct lineptr *hlineptr, *dlineptr;
|
||||||
|
|
||||||
|
if (cancel_pressed)
|
||||||
|
return;
|
||||||
|
|
||||||
if (cells[0] == NULL)
|
if (cells[0] == NULL)
|
||||||
{
|
{
|
||||||
fprintf(fout, _("(No rows)\n"));
|
fprintf(fout, _("(No rows)\n"));
|
||||||
@ -764,6 +797,8 @@ print_aligned_vertical(const char *title, const char *const * headers,
|
|||||||
|
|
||||||
if (i % col_count == 0)
|
if (i % col_count == 0)
|
||||||
{
|
{
|
||||||
|
if (cancel_pressed)
|
||||||
|
break;
|
||||||
if (!opt_tuples_only)
|
if (!opt_tuples_only)
|
||||||
{
|
{
|
||||||
char *record_str = pg_local_malloc(32);
|
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);
|
fprintf(fout, "%s\n", divider);
|
||||||
|
|
||||||
/* print footers */
|
/* print footers */
|
||||||
|
|
||||||
if (!opt_tuples_only && footers && *footers)
|
if (!opt_tuples_only && footers && *footers && !cancel_pressed)
|
||||||
{
|
{
|
||||||
if (opt_border < 2)
|
if (opt_border < 2)
|
||||||
fputc('\n', fout);
|
fputc('\n', fout);
|
||||||
@ -943,6 +978,9 @@ print_html_text(const char *title, const char *const * headers,
|
|||||||
unsigned int i;
|
unsigned int i;
|
||||||
const char *const * ptr;
|
const char *const * ptr;
|
||||||
|
|
||||||
|
if (cancel_pressed)
|
||||||
|
return;
|
||||||
|
|
||||||
fprintf(fout, "<table border=\"%d\"", opt_border);
|
fprintf(fout, "<table border=\"%d\"", opt_border);
|
||||||
if (opt_table_attr)
|
if (opt_table_attr)
|
||||||
fprintf(fout, " %s", 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++)
|
for (i = 0, ptr = cells; *ptr; i++, ptr++)
|
||||||
{
|
{
|
||||||
if (i % col_count == 0)
|
if (i % col_count == 0)
|
||||||
|
{
|
||||||
|
if (cancel_pressed)
|
||||||
|
break;
|
||||||
fputs(" <tr valign=\"top\">\n", fout);
|
fputs(" <tr valign=\"top\">\n", fout);
|
||||||
|
}
|
||||||
|
|
||||||
fprintf(fout, " <td align=\"%s\">", opt_align[(i) % col_count] == 'r' ? "right" : "left");
|
fprintf(fout, " <td align=\"%s\">", opt_align[(i) % col_count] == 'r' ? "right" : "left");
|
||||||
/* is string only whitespace? */
|
/* is string only whitespace? */
|
||||||
@ -1002,7 +1044,7 @@ print_html_text(const char *title, const char *const * headers,
|
|||||||
|
|
||||||
/* print footers */
|
/* print footers */
|
||||||
|
|
||||||
if (!opt_tuples_only && footers && *footers)
|
if (!opt_tuples_only && footers && *footers && !cancel_pressed)
|
||||||
{
|
{
|
||||||
fputs("<p>", fout);
|
fputs("<p>", fout);
|
||||||
for (ptr = footers; *ptr; ptr++)
|
for (ptr = footers; *ptr; ptr++)
|
||||||
@ -1029,6 +1071,9 @@ print_html_vertical(const char *title, const char *const * headers,
|
|||||||
unsigned int record = 1;
|
unsigned int record = 1;
|
||||||
const char *const * ptr;
|
const char *const * ptr;
|
||||||
|
|
||||||
|
if (cancel_pressed)
|
||||||
|
return;
|
||||||
|
|
||||||
fprintf(fout, "<table border=\"%d\"", opt_border);
|
fprintf(fout, "<table border=\"%d\"", opt_border);
|
||||||
if (opt_table_attr)
|
if (opt_table_attr)
|
||||||
fprintf(fout, " %s", 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 (i % col_count == 0)
|
||||||
{
|
{
|
||||||
|
if (cancel_pressed)
|
||||||
|
break;
|
||||||
if (!opt_tuples_only)
|
if (!opt_tuples_only)
|
||||||
fprintf(fout, "\n <tr><td colspan=\"2\" align=\"center\">Record %d</td></tr>\n", record++);
|
fprintf(fout, "\n <tr><td colspan=\"2\" align=\"center\">Record %d</td></tr>\n", record++);
|
||||||
else
|
else
|
||||||
@ -1081,7 +1128,7 @@ print_html_vertical(const char *title, const char *const * headers,
|
|||||||
fputs("</table>\n", fout);
|
fputs("</table>\n", fout);
|
||||||
|
|
||||||
/* print footers */
|
/* print footers */
|
||||||
if (!opt_tuples_only && footers && *footers)
|
if (!opt_tuples_only && footers && *footers && !cancel_pressed)
|
||||||
{
|
{
|
||||||
fputs("<p>", fout);
|
fputs("<p>", fout);
|
||||||
for (ptr = footers; *ptr; ptr++)
|
for (ptr = footers; *ptr; ptr++)
|
||||||
@ -1151,6 +1198,8 @@ print_latex_text(const char *title, const char *const * headers,
|
|||||||
unsigned int i;
|
unsigned int i;
|
||||||
const char *const * ptr;
|
const char *const * ptr;
|
||||||
|
|
||||||
|
if (cancel_pressed)
|
||||||
|
return;
|
||||||
|
|
||||||
/* print title */
|
/* print title */
|
||||||
if (!opt_tuples_only && 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);
|
latex_escaped_print(*ptr, fout);
|
||||||
|
|
||||||
if ((i + 1) % col_count == 0)
|
if ((i + 1) % col_count == 0)
|
||||||
|
{
|
||||||
fputs(" \\\\\n", fout);
|
fputs(" \\\\\n", fout);
|
||||||
|
if (cancel_pressed)
|
||||||
|
break;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
fputs(" & ", fout);
|
fputs(" & ", fout);
|
||||||
}
|
}
|
||||||
@ -1229,7 +1282,7 @@ print_latex_text(const char *title, const char *const * headers,
|
|||||||
|
|
||||||
/* print footers */
|
/* print footers */
|
||||||
|
|
||||||
if (footers && !opt_tuples_only)
|
if (footers && !opt_tuples_only && !cancel_pressed)
|
||||||
for (ptr = footers; *ptr; ptr++)
|
for (ptr = footers; *ptr; ptr++)
|
||||||
{
|
{
|
||||||
latex_escaped_print(*ptr, fout);
|
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 */
|
(void) opt_align; /* currently unused parameter */
|
||||||
|
|
||||||
|
if (cancel_pressed)
|
||||||
|
return;
|
||||||
|
|
||||||
/* print title */
|
/* print title */
|
||||||
if (!opt_tuples_only && title)
|
if (!opt_tuples_only && title)
|
||||||
{
|
{
|
||||||
@ -1285,6 +1341,8 @@ print_latex_vertical(const char *title, const char *const * headers,
|
|||||||
/* new record */
|
/* new record */
|
||||||
if (i % col_count == 0)
|
if (i % col_count == 0)
|
||||||
{
|
{
|
||||||
|
if (cancel_pressed)
|
||||||
|
break;
|
||||||
if (!opt_tuples_only)
|
if (!opt_tuples_only)
|
||||||
{
|
{
|
||||||
if (opt_border == 2)
|
if (opt_border == 2)
|
||||||
@ -1313,7 +1371,7 @@ print_latex_vertical(const char *title, const char *const * headers,
|
|||||||
|
|
||||||
/* print footers */
|
/* print footers */
|
||||||
|
|
||||||
if (footers && !opt_tuples_only)
|
if (footers && !opt_tuples_only && !cancel_pressed)
|
||||||
for (ptr = footers; *ptr; ptr++)
|
for (ptr = footers; *ptr; ptr++)
|
||||||
{
|
{
|
||||||
if (opt_numeric_locale)
|
if (opt_numeric_locale)
|
||||||
@ -1367,6 +1425,8 @@ print_troff_ms_text(const char *title, const char *const * headers,
|
|||||||
unsigned int i;
|
unsigned int i;
|
||||||
const char *const * ptr;
|
const char *const * ptr;
|
||||||
|
|
||||||
|
if (cancel_pressed)
|
||||||
|
return;
|
||||||
|
|
||||||
/* print title */
|
/* print title */
|
||||||
if (!opt_tuples_only && 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);
|
troff_ms_escaped_print(*ptr, fout);
|
||||||
|
|
||||||
if ((i + 1) % col_count == 0)
|
if ((i + 1) % col_count == 0)
|
||||||
|
{
|
||||||
fputc('\n', fout);
|
fputc('\n', fout);
|
||||||
|
if (cancel_pressed)
|
||||||
|
break;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
fputc('\t', fout);
|
fputc('\t', fout);
|
||||||
}
|
}
|
||||||
@ -1435,7 +1499,7 @@ print_troff_ms_text(const char *title, const char *const * headers,
|
|||||||
|
|
||||||
/* print footers */
|
/* print footers */
|
||||||
|
|
||||||
if (footers && !opt_tuples_only)
|
if (footers && !opt_tuples_only && !cancel_pressed)
|
||||||
for (ptr = footers; *ptr; ptr++)
|
for (ptr = footers; *ptr; ptr++)
|
||||||
{
|
{
|
||||||
troff_ms_escaped_print(*ptr, fout);
|
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 */
|
(void) opt_align; /* currently unused parameter */
|
||||||
|
|
||||||
|
if (cancel_pressed)
|
||||||
|
return;
|
||||||
|
|
||||||
/* print title */
|
/* print title */
|
||||||
if (!opt_tuples_only && title)
|
if (!opt_tuples_only && title)
|
||||||
{
|
{
|
||||||
@ -1491,6 +1558,8 @@ print_troff_ms_vertical(const char *title, const char *const * headers,
|
|||||||
/* new record */
|
/* new record */
|
||||||
if (i % col_count == 0)
|
if (i % col_count == 0)
|
||||||
{
|
{
|
||||||
|
if (cancel_pressed)
|
||||||
|
break;
|
||||||
if (!opt_tuples_only)
|
if (!opt_tuples_only)
|
||||||
{
|
{
|
||||||
if (current_format != 1)
|
if (current_format != 1)
|
||||||
@ -1542,7 +1611,7 @@ print_troff_ms_vertical(const char *title, const char *const * headers,
|
|||||||
|
|
||||||
/* print footers */
|
/* print footers */
|
||||||
|
|
||||||
if (footers && !opt_tuples_only)
|
if (footers && !opt_tuples_only && !cancel_pressed)
|
||||||
for (ptr = footers; *ptr; ptr++)
|
for (ptr = footers; *ptr; ptr++)
|
||||||
{
|
{
|
||||||
troff_ms_escaped_print(*ptr, fout);
|
troff_ms_escaped_print(*ptr, fout);
|
||||||
@ -1618,6 +1687,9 @@ printTable(const char *title,
|
|||||||
FILE *output;
|
FILE *output;
|
||||||
bool use_expanded;
|
bool use_expanded;
|
||||||
|
|
||||||
|
if (cancel_pressed)
|
||||||
|
return;
|
||||||
|
|
||||||
if (opt->format == PRINT_NOTHING)
|
if (opt->format == PRINT_NOTHING)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -1731,6 +1803,17 @@ printTable(const char *title,
|
|||||||
/* Only close if we used the pager */
|
/* Only close if we used the pager */
|
||||||
if (fout == stdout && output != stdout)
|
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);
|
pclose(output);
|
||||||
#ifndef WIN32
|
#ifndef WIN32
|
||||||
pqsignal(SIGPIPE, SIG_DFL);
|
pqsignal(SIGPIPE, SIG_DFL);
|
||||||
@ -1751,6 +1834,9 @@ printQuery(const PGresult *result, const printQueryOpt *opt, FILE *fout, FILE *f
|
|||||||
char *align;
|
char *align;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
if (cancel_pressed)
|
||||||
|
return;
|
||||||
|
|
||||||
/* extract headers */
|
/* extract headers */
|
||||||
nfields = PQnfields(result);
|
nfields = PQnfields(result);
|
||||||
|
|
||||||
@ -1798,18 +1884,24 @@ printQuery(const PGresult *result, const printQueryOpt *opt, FILE *fout, FILE *f
|
|||||||
{
|
{
|
||||||
Oid ftype = PQftype(result, i);
|
Oid ftype = PQftype(result, i);
|
||||||
|
|
||||||
if (ftype == 20 || /* int8 */
|
switch (ftype)
|
||||||
ftype == 21 || /* int2 */
|
{
|
||||||
ftype == 23 || /* int4 */
|
case INT2OID:
|
||||||
(ftype >= 26 && ftype <= 30) || /* ?id */
|
case INT4OID:
|
||||||
ftype == 700 || /* float4 */
|
case INT8OID:
|
||||||
ftype == 701 || /* float8 */
|
case FLOAT4OID:
|
||||||
ftype == 790 || /* money */
|
case FLOAT8OID:
|
||||||
ftype == 1700 /* numeric */
|
case NUMERICOID:
|
||||||
)
|
case OIDOID:
|
||||||
|
case XIDOID:
|
||||||
|
case CIDOID:
|
||||||
|
case CASHOID:
|
||||||
align[i] = 'r';
|
align[i] = 'r';
|
||||||
else
|
break;
|
||||||
|
default:
|
||||||
align[i] = 'l';
|
align[i] = 'l';
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* call table printer */
|
/* call table printer */
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
*
|
*
|
||||||
* Copyright (c) 2000-2006, PostgreSQL Global Development Group
|
* 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"
|
#include "postgres_fe.h"
|
||||||
|
|
||||||
@ -134,7 +134,6 @@ main(int argc, char *argv[])
|
|||||||
|
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
setvbuf(stderr, NULL, _IONBF, 0);
|
setvbuf(stderr, NULL, _IONBF, 0);
|
||||||
setup_win32_locks();
|
|
||||||
#endif
|
#endif
|
||||||
setDecimalLocale();
|
setDecimalLocale();
|
||||||
pset.cur_cmd_source = stdin;
|
pset.cur_cmd_source = stdin;
|
||||||
@ -371,6 +370,9 @@ main(int argc, char *argv[])
|
|||||||
if (options.action_string) /* -f - was used */
|
if (options.action_string) /* -f - was used */
|
||||||
pset.inputfile = "<stdin>";
|
pset.inputfile = "<stdin>";
|
||||||
|
|
||||||
|
/* establish control-C handling for interactive operation */
|
||||||
|
setup_cancel_handler();
|
||||||
|
|
||||||
successResult = MainLoop(stdin);
|
successResult = MainLoop(stdin);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
*
|
*
|
||||||
* Copyright (c) 2000-2006, PostgreSQL Global Development Group
|
* 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 "postgres_fe.h"
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
@ -127,7 +127,11 @@ PrintVariables(VariableSpace space)
|
|||||||
struct _variable *ptr;
|
struct _variable *ptr;
|
||||||
|
|
||||||
for (ptr = space->next; ptr; ptr = ptr->next)
|
for (ptr = space->next; ptr; ptr = ptr->next)
|
||||||
|
{
|
||||||
printf("%s = '%s'\n", ptr->name, ptr->value);
|
printf("%s = '%s'\n", ptr->name, ptr->value);
|
||||||
|
if (cancel_pressed)
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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>
|
#include <termios.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool prompt_state = false;
|
|
||||||
extern char *simple_prompt(const char *prompt, int maxlen, bool echo);
|
extern char *simple_prompt(const char *prompt, int maxlen, bool echo);
|
||||||
|
|
||||||
char *
|
char *
|
||||||
@ -57,8 +56,6 @@ simple_prompt(const char *prompt, int maxlen, bool echo)
|
|||||||
if (!destination)
|
if (!destination)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
prompt_state = true; /* disable SIGINT */
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Do not try to collapse these into one "w+" mode file. Doesn't work on
|
* Do not try to collapse these into one "w+" mode file. Doesn't work on
|
||||||
* some platforms (eg, HPUX 10.20).
|
* some platforms (eg, HPUX 10.20).
|
||||||
@ -159,7 +156,5 @@ simple_prompt(const char *prompt, int maxlen, bool echo)
|
|||||||
fclose(termout);
|
fclose(termout);
|
||||||
}
|
}
|
||||||
|
|
||||||
prompt_state = false; /* SIGINT okay again */
|
|
||||||
|
|
||||||
return destination;
|
return destination;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user