Commit e6311b4a authored by Bruce Momjian's avatar Bruce Momjian

The attached patch implements some changes that were discussed a
couple weeks ago on the hackers and interfaces lists:

1. When the backend sends a NOTICE message and closes the connection
   (typically, because it was told to by the postmaster after
   another backend coredumped), libpq will now print the notice
   and close the connection cleanly.  Formerly, the frontend app
   would usually terminate ungracefully due to a SIGPIPE.  (I am
   not sure if 6.3.2 behaved that way, but the current cvs sources
   do...)

2. libpq's various printouts to stderr are now fed through a single
   "notice processor" routine, which can be overridden by the
   application to direct notices someplace else.  This should ease
   porting libpq to Windows.

I also noticed and fixed a problem in PQprint: when sending output
to a pager subprocess, it would disable SIGPIPE in case the pager
terminates early (this is good) --- but afterwards it reset SIGPIPE
to SIG_DFL, rather than restoring the application's prior setting
(bad).

			regards, tom lane
parent e46df2ff
......@@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-auth.c,v 1.20 1998/07/20 16:57:13 momjian Exp $
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-auth.c,v 1.21 1998/08/09 02:59:25 momjian Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -445,8 +445,6 @@ pg_krb5_sendauth(const char *PQerrormsg, int sock,
(void) sprintf(PQerrormsg,
"pg_krb5_sendauth: authentication rejected: \"%*s\"\n",
error->text.length, error->text.data);
fputs(PQerrormsg, stderr);
pqdebug("%s", PQerrormsg);
}
else
{
......
......@@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.77 1998/07/26 04:31:36 scrappy Exp $
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.78 1998/08/09 02:59:26 momjian Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -29,7 +29,6 @@
#include <ctype.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <ctype.h> /* for isspace() */
#include "postgres.h"
......@@ -55,6 +54,7 @@ static void closePGconn(PGconn *conn);
static int conninfo_parse(const char *conninfo, char *errorMessage);
static char *conninfo_getval(char *keyword);
static void conninfo_free(void);
static void defaultNoticeProcessor(void * arg, const char * message);
/* XXX Why is this not static? */
void PQsetenv(PGconn *conn);
......@@ -181,11 +181,7 @@ PQconnectdb(const char *conninfo)
*/
conn = makeEmptyPGconn();
if (conn == NULL)
{
fprintf(stderr,
"FATAL: PQconnectdb() -- unable to allocate memory for a PGconn");
return (PGconn *) NULL;
}
/* ----------
* Parse the conninfo string and save settings in conn structure
......@@ -297,11 +293,7 @@ PQsetdbLogin(const char *pghost, const char *pgport, const char *pgoptions, cons
conn = makeEmptyPGconn();
if (conn == NULL)
{
fprintf(stderr,
"FATAL: PQsetdbLogin() -- unable to allocate memory for a PGconn");
return (PGconn *) NULL;
}
if ((pghost == NULL) || pghost[0] == '\0')
{
......@@ -856,6 +848,7 @@ makeEmptyPGconn(void)
/* Zero all pointers */
MemSet((char *) conn, 0, sizeof(PGconn));
conn->noticeHook = defaultNoticeProcessor;
conn->status = CONNECTION_BAD;
conn->asyncStatus = PGASYNC_IDLE;
conn->notifyList = DLNewList();
......@@ -925,35 +918,20 @@ closePGconn(PGconn *conn)
if (conn->sock >= 0)
{
/*
* Try to send close message.
* If connection is already gone, that's cool. No reason for kernel
* to kill us when we try to write to it. So ignore SIGPIPE signals.
* Try to send "close connection" message to backend.
* BUT: backend might have already closed connection.
* To avoid being killed by SIGPIPE, we need to detect this before
* writing. Check for "read ready" condition which indicates EOF.
*/
#ifndef WIN32
#if defined(USE_POSIX_SIGNALS)
struct sigaction ignore_action;
struct sigaction oldaction;
ignore_action.sa_handler = SIG_IGN;
sigemptyset(&ignore_action.sa_mask);
ignore_action.sa_flags = 0;
sigaction(SIGPIPE, (struct sigaction *) & ignore_action, &oldaction);
(void) pqPuts("X", conn);
(void) pqFlush(conn);
sigaction(SIGPIPE, &oldaction, NULL);
#else
void (*oldsignal)(int);
oldsignal = signal(SIGPIPE, SIG_IGN);
(void) pqPuts("X", conn);
(void) pqFlush(conn);
signal(SIGPIPE, oldsignal);
#endif
#endif /* Win32 uses no signals at all */
while (pqReadReady(conn)) {
if (pqReadData(conn) < 0)
break;
}
if (conn->sock >= 0) {
/* Should be safe now... */
(void) pqPuts("X", conn);
(void) pqFlush(conn);
}
}
/*
......@@ -987,9 +965,7 @@ closePGconn(PGconn *conn)
void
PQfinish(PGconn *conn)
{
if (!conn)
fprintf(stderr, "PQfinish() -- pointer to PGconn is null\n");
else
if (conn)
{
closePGconn(conn);
freePGconn(conn);
......@@ -1003,9 +979,7 @@ PQfinish(PGconn *conn)
void
PQreset(PGconn *conn)
{
if (!conn)
fprintf(stderr, "PQreset() -- pointer to PGconn is null\n");
else
if (conn)
{
closePGconn(conn);
conn->status = connectDB(conn);
......@@ -1383,10 +1357,7 @@ char *
PQdb(PGconn *conn)
{
if (!conn)
{
fprintf(stderr, "PQdb() -- pointer to PGconn is null\n");
return (char *) NULL;
}
return conn->dbName;
}
......@@ -1394,10 +1365,7 @@ char *
PQuser(PGconn *conn)
{
if (!conn)
{
fprintf(stderr, "PQuser() -- pointer to PGconn is null\n");
return (char *) NULL;
}
return conn->pguser;
}
......@@ -1405,11 +1373,7 @@ char *
PQhost(PGconn *conn)
{
if (!conn)
{
fprintf(stderr, "PQhost() -- pointer to PGconn is null\n");
return (char *) NULL;
}
return conn->pghost;
}
......@@ -1417,10 +1381,7 @@ char *
PQoptions(PGconn *conn)
{
if (!conn)
{
fprintf(stderr, "PQoptions() -- pointer to PGconn is null\n");
return (char *) NULL;
}
return conn->pgoptions;
}
......@@ -1428,10 +1389,7 @@ char *
PQtty(PGconn *conn)
{
if (!conn)
{
fprintf(stderr, "PQtty() -- pointer to PGconn is null\n");
return (char *) NULL;
}
return conn->pgtty;
}
......@@ -1439,10 +1397,7 @@ char *
PQport(PGconn *conn)
{
if (!conn)
{
fprintf(stderr, "PQport() -- pointer to PGconn is null\n");
return (char *) NULL;
}
return conn->pgport;
}
......@@ -1450,21 +1405,16 @@ ConnStatusType
PQstatus(PGconn *conn)
{
if (!conn)
{
fprintf(stderr, "PQstatus() -- pointer to PGconn is null\n");
return CONNECTION_BAD;
}
return conn->status;
}
char *
PQerrorMessage(PGconn *conn)
{
static char noConn[] = "PQerrorMessage: conn pointer is NULL\n";
if (!conn)
{
fprintf(stderr, "PQerrorMessage() -- pointer to PGconn is null\n");
return (char *) NULL;
}
return noConn;
return conn->errorMessage;
}
......@@ -1472,10 +1422,7 @@ int
PQsocket(PGconn *conn)
{
if (!conn)
{
fprintf(stderr, "PQsocket() -- pointer to PGconn is null\n");
return -1;
}
return conn->sock;
}
......@@ -1501,3 +1448,26 @@ PQuntrace(PGconn *conn)
conn->Pfdebug = NULL;
}
}
void
PQsetNoticeProcessor (PGconn *conn, PQnoticeProcessor proc, void *arg)
{
if (conn == NULL)
return;
conn->noticeHook = proc;
conn->noticeArg = arg;
}
/*
* The default notice/error message processor just prints the
* message on stderr. Applications can override this if they
* want the messages to go elsewhere (a window, for example).
* Note that simply discarding notices is probably a bad idea.
*/
static void
defaultNoticeProcessor(void * arg, const char * message)
{
/* Note: we expect the supplied string to end with a newline already. */
fprintf(stderr, "%s", message);
}
This diff is collapsed.
......@@ -24,7 +24,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-misc.c,v 1.16 1998/07/03 04:24:14 momjian Exp $
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-misc.c,v 1.17 1998/08/09 02:59:29 momjian Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -50,6 +50,10 @@
#include "postgres.h"
#include "libpq-fe.h"
#define DONOTICE(conn,message) \
((*(conn)->noticeHook) ((conn)->noticeArg, (message)))
/* --------------------------------------------------------------------- */
/* pqGetc:
get a character from the connection
......@@ -218,7 +222,9 @@ pqGetInt(int *result, int bytes, PGconn *conn)
*result = (int) ntohl(tmp4);
break;
default:
fprintf(stderr, "** int size %d not supported\n", bytes);
sprintf(conn->errorMessage,
"pqGetInt: int size %d not supported\n", bytes);
DONOTICE(conn, conn->errorMessage);
return EOF;
}
......@@ -252,7 +258,9 @@ pqPutInt(int value, int bytes, PGconn *conn)
return EOF;
break;
default:
fprintf(stderr, "** int size %d not supported\n", bytes);
sprintf(conn->errorMessage,
"pqPutInt: int size %d not supported\n", bytes);
DONOTICE(conn, conn->errorMessage);
return EOF;
}
......@@ -265,7 +273,7 @@ pqPutInt(int value, int bytes, PGconn *conn)
/* --------------------------------------------------------------------- */
/* pqReadReady: is select() saying the file is ready to read?
*/
static int
int
pqReadReady(PGconn *conn)
{
fd_set input_mask;
......
......@@ -9,7 +9,7 @@
* didn't really belong there.
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-print.c,v 1.8 1998/07/26 04:31:36 scrappy Exp $
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-print.c,v 1.9 1998/08/09 02:59:30 momjian Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -106,6 +106,7 @@ PQprint(FILE *fout,
int fs_len = strlen(po->fieldSep);
int total_line_length = 0;
int usePipe = 0;
pqsigfunc oldsigpipehandler = NULL;
char *pagerenv;
char buf[8192 * 2 + 1];
......@@ -193,7 +194,7 @@ PQprint(FILE *fout,
{
usePipe = 1;
#ifndef WIN32
pqsignal(SIGPIPE, SIG_IGN);
oldsigpipehandler = pqsignal(SIGPIPE, SIG_IGN);
#endif
}
else
......@@ -309,7 +310,7 @@ PQprint(FILE *fout,
_pclose(fout);
#else
pclose(fout);
pqsignal(SIGPIPE, SIG_DFL);
pqsignal(SIGPIPE, oldsigpipehandler);
#endif
}
if (po->html3 && !po->expanded)
......
......@@ -6,7 +6,7 @@
*
* Copyright (c) 1994, Regents of the University of California
*
* $Id: libpq-fe.h,v 1.36 1998/07/18 18:34:34 momjian Exp $
* $Id: libpq-fe.h,v 1.37 1998/08/09 02:59:31 momjian Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -121,6 +121,9 @@ extern "C"
int be_pid; /* process id of backend */
} PGnotify;
/* PQnoticeProcessor is a typedef for a callback function type */
typedef void (*PQnoticeProcessor) (void * arg, const char * message);
/* PGAsyncStatusType is private to libpq, really shouldn't be seen by users */
typedef enum
{
......@@ -165,6 +168,10 @@ extern "C"
/* Optional file to write trace info to */
FILE *Pfdebug;
/* Callback procedure for notice/error message processing */
PQnoticeProcessor noticeHook;
void *noticeArg;
/* Status indicators */
ConnStatusType status;
PGAsyncStatusType asyncStatus;
......@@ -300,6 +307,11 @@ extern "C"
extern void PQtrace(PGconn *conn, FILE *debug_port);
extern void PQuntrace(PGconn *conn);
/* Override default notice processor */
extern void PQsetNoticeProcessor (PGconn *conn,
PQnoticeProcessor proc,
void *arg);
/* === in fe-exec.c === */
/* Simple synchronous query */
extern PGresult *PQexec(PGconn *conn, const char *query);
......@@ -390,6 +402,7 @@ extern "C"
extern int pqGetInt(int *result, int bytes, PGconn *conn);
extern int pqPutInt(int value, int bytes, PGconn *conn);
extern int pqReadData(PGconn *conn);
extern int pqReadReady(PGconn *conn);
extern int pqFlush(PGconn *conn);
extern int pqWait(int forRead, int forWrite, PGconn *conn);
......
.\" This is -*-nroff-*-
.\" XXX standard disclaimer belongs here....
.\" $Header: /cvsroot/pgsql/src/man/Attic/libpq.3,v 1.22 1998/07/15 17:34:06 momjian Exp $
.TH LIBPQ INTRO 07/08/98 PostgreSQL PostgreSQL
.\" $Header: /cvsroot/pgsql/src/man/Attic/libpq.3,v 1.23 1998/08/09 02:59:33 momjian Exp $
.TH LIBPQ INTRO 08/08/98 PostgreSQL PostgreSQL
.SH DESCRIPTION
Libpq is the programmer's interface to Postgres. Libpq is a set of
library routines which allows
......@@ -823,6 +823,36 @@ Disable tracing started by
.nf
void PQuntrace(PGconn *conn)
.fi
.PP
.SH "LIBPQ Control Functions"
.PP
.B PQsetNoticeProcessor
.IP
Control reporting of notice and warning messages generated by libpq.
.nf
void PQsetNoticeProcessor (PGconn * conn,
void (*noticeProcessor) (void * arg, const char * message),
void * arg)
.fi
By default, libpq prints "notice" messages from the backend on stderr,
as well as a few error messages that it generates by itself.
This behavior can be overridden by supplying a callback function that
does something else with the messages. The callback function is passed
the text of the error message (which includes a trailing newline), plus
a void pointer that is the same one passed to PQsetNoticeProcessor.
(This pointer can be used to access application-specific state if needed.)
The default notice processor is simply
.nf
static void
defaultNoticeProcessor(void * arg, const char * message)
{
fprintf(stderr, "%s", message);
}
.fi
To use a special notice processor, call PQsetNoticeProcessor just after
any creation of a new PGconn object.
.PP
.SH "User Authentication Functions"
.PP
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment