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
This commit is contained in:
Bruce Momjian 1998-08-09 02:59:33 +00:00
parent e46df2ff6e
commit e6311b4ad0
7 changed files with 223 additions and 262 deletions

View File

@ -7,7 +7,7 @@
* *
* *
* IDENTIFICATION * 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, (void) sprintf(PQerrormsg,
"pg_krb5_sendauth: authentication rejected: \"%*s\"\n", "pg_krb5_sendauth: authentication rejected: \"%*s\"\n",
error->text.length, error->text.data); error->text.length, error->text.data);
fputs(PQerrormsg, stderr);
pqdebug("%s", PQerrormsg);
} }
else else
{ {

View File

@ -7,7 +7,7 @@
* *
* *
* IDENTIFICATION * 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 <ctype.h>
#include <string.h> #include <string.h>
#include <errno.h> #include <errno.h>
#include <signal.h>
#include <ctype.h> /* for isspace() */ #include <ctype.h> /* for isspace() */
#include "postgres.h" #include "postgres.h"
@ -55,6 +54,7 @@ static void closePGconn(PGconn *conn);
static int conninfo_parse(const char *conninfo, char *errorMessage); static int conninfo_parse(const char *conninfo, char *errorMessage);
static char *conninfo_getval(char *keyword); static char *conninfo_getval(char *keyword);
static void conninfo_free(void); static void conninfo_free(void);
static void defaultNoticeProcessor(void * arg, const char * message);
/* XXX Why is this not static? */ /* XXX Why is this not static? */
void PQsetenv(PGconn *conn); void PQsetenv(PGconn *conn);
@ -181,11 +181,7 @@ PQconnectdb(const char *conninfo)
*/ */
conn = makeEmptyPGconn(); conn = makeEmptyPGconn();
if (conn == NULL) if (conn == NULL)
{
fprintf(stderr,
"FATAL: PQconnectdb() -- unable to allocate memory for a PGconn");
return (PGconn *) NULL; return (PGconn *) NULL;
}
/* ---------- /* ----------
* Parse the conninfo string and save settings in conn structure * 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(); conn = makeEmptyPGconn();
if (conn == NULL) if (conn == NULL)
{
fprintf(stderr,
"FATAL: PQsetdbLogin() -- unable to allocate memory for a PGconn");
return (PGconn *) NULL; return (PGconn *) NULL;
}
if ((pghost == NULL) || pghost[0] == '\0') if ((pghost == NULL) || pghost[0] == '\0')
{ {
@ -856,6 +848,7 @@ makeEmptyPGconn(void)
/* Zero all pointers */ /* Zero all pointers */
MemSet((char *) conn, 0, sizeof(PGconn)); MemSet((char *) conn, 0, sizeof(PGconn));
conn->noticeHook = defaultNoticeProcessor;
conn->status = CONNECTION_BAD; conn->status = CONNECTION_BAD;
conn->asyncStatus = PGASYNC_IDLE; conn->asyncStatus = PGASYNC_IDLE;
conn->notifyList = DLNewList(); conn->notifyList = DLNewList();
@ -925,35 +918,20 @@ closePGconn(PGconn *conn)
if (conn->sock >= 0) if (conn->sock >= 0)
{ {
/* /*
* Try to send close message. * Try to send "close connection" message to backend.
* If connection is already gone, that's cool. No reason for kernel * BUT: backend might have already closed connection.
* to kill us when we try to write to it. So ignore SIGPIPE signals. * To avoid being killed by SIGPIPE, we need to detect this before
* writing. Check for "read ready" condition which indicates EOF.
*/ */
#ifndef WIN32 while (pqReadReady(conn)) {
#if defined(USE_POSIX_SIGNALS) if (pqReadData(conn) < 0)
struct sigaction ignore_action; break;
struct sigaction oldaction; }
if (conn->sock >= 0) {
ignore_action.sa_handler = SIG_IGN; /* Should be safe now... */
sigemptyset(&ignore_action.sa_mask); (void) pqPuts("X", conn);
ignore_action.sa_flags = 0; (void) pqFlush(conn);
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 */
} }
/* /*
@ -987,9 +965,7 @@ closePGconn(PGconn *conn)
void void
PQfinish(PGconn *conn) PQfinish(PGconn *conn)
{ {
if (!conn) if (conn)
fprintf(stderr, "PQfinish() -- pointer to PGconn is null\n");
else
{ {
closePGconn(conn); closePGconn(conn);
freePGconn(conn); freePGconn(conn);
@ -1003,9 +979,7 @@ PQfinish(PGconn *conn)
void void
PQreset(PGconn *conn) PQreset(PGconn *conn)
{ {
if (!conn) if (conn)
fprintf(stderr, "PQreset() -- pointer to PGconn is null\n");
else
{ {
closePGconn(conn); closePGconn(conn);
conn->status = connectDB(conn); conn->status = connectDB(conn);
@ -1383,10 +1357,7 @@ char *
PQdb(PGconn *conn) PQdb(PGconn *conn)
{ {
if (!conn) if (!conn)
{
fprintf(stderr, "PQdb() -- pointer to PGconn is null\n");
return (char *) NULL; return (char *) NULL;
}
return conn->dbName; return conn->dbName;
} }
@ -1394,10 +1365,7 @@ char *
PQuser(PGconn *conn) PQuser(PGconn *conn)
{ {
if (!conn) if (!conn)
{
fprintf(stderr, "PQuser() -- pointer to PGconn is null\n");
return (char *) NULL; return (char *) NULL;
}
return conn->pguser; return conn->pguser;
} }
@ -1405,11 +1373,7 @@ char *
PQhost(PGconn *conn) PQhost(PGconn *conn)
{ {
if (!conn) if (!conn)
{
fprintf(stderr, "PQhost() -- pointer to PGconn is null\n");
return (char *) NULL; return (char *) NULL;
}
return conn->pghost; return conn->pghost;
} }
@ -1417,10 +1381,7 @@ char *
PQoptions(PGconn *conn) PQoptions(PGconn *conn)
{ {
if (!conn) if (!conn)
{
fprintf(stderr, "PQoptions() -- pointer to PGconn is null\n");
return (char *) NULL; return (char *) NULL;
}
return conn->pgoptions; return conn->pgoptions;
} }
@ -1428,10 +1389,7 @@ char *
PQtty(PGconn *conn) PQtty(PGconn *conn)
{ {
if (!conn) if (!conn)
{
fprintf(stderr, "PQtty() -- pointer to PGconn is null\n");
return (char *) NULL; return (char *) NULL;
}
return conn->pgtty; return conn->pgtty;
} }
@ -1439,10 +1397,7 @@ char *
PQport(PGconn *conn) PQport(PGconn *conn)
{ {
if (!conn) if (!conn)
{
fprintf(stderr, "PQport() -- pointer to PGconn is null\n");
return (char *) NULL; return (char *) NULL;
}
return conn->pgport; return conn->pgport;
} }
@ -1450,21 +1405,16 @@ ConnStatusType
PQstatus(PGconn *conn) PQstatus(PGconn *conn)
{ {
if (!conn) if (!conn)
{
fprintf(stderr, "PQstatus() -- pointer to PGconn is null\n");
return CONNECTION_BAD; return CONNECTION_BAD;
}
return conn->status; return conn->status;
} }
char * char *
PQerrorMessage(PGconn *conn) PQerrorMessage(PGconn *conn)
{ {
static char noConn[] = "PQerrorMessage: conn pointer is NULL\n";
if (!conn) if (!conn)
{ return noConn;
fprintf(stderr, "PQerrorMessage() -- pointer to PGconn is null\n");
return (char *) NULL;
}
return conn->errorMessage; return conn->errorMessage;
} }
@ -1472,10 +1422,7 @@ int
PQsocket(PGconn *conn) PQsocket(PGconn *conn)
{ {
if (!conn) if (!conn)
{
fprintf(stderr, "PQsocket() -- pointer to PGconn is null\n");
return -1; return -1;
}
return conn->sock; return conn->sock;
} }
@ -1501,3 +1448,26 @@ PQuntrace(PGconn *conn)
conn->Pfdebug = NULL; 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);
}

View File

@ -7,7 +7,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.60 1998/07/14 02:41:25 momjian Exp $ * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.61 1998/08/09 02:59:27 momjian Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -44,6 +44,10 @@ const char *pgresStatus[] = {
}; };
#define DONOTICE(conn,message) \
((*(conn)->noticeHook) ((conn)->noticeArg, (message)))
static PGresult *makeEmptyPGresult(PGconn *conn, ExecStatusType status); static PGresult *makeEmptyPGresult(PGconn *conn, ExecStatusType status);
static void freeTuple(PGresAttValue *tuple, int numAttributes); static void freeTuple(PGresAttValue *tuple, int numAttributes);
static void addTuple(PGresult *res, PGresAttValue *tup); static void addTuple(PGresult *res, PGresAttValue *tup);
@ -198,6 +202,14 @@ PQsendQuery(PGconn *conn, const char *query)
sprintf(conn->errorMessage, "PQsendQuery() -- query pointer is null."); sprintf(conn->errorMessage, "PQsendQuery() -- query pointer is null.");
return 0; return 0;
} }
/* check to see if the query string is too long */
if (strlen(query) > MAX_MESSAGE_LEN-2)
{
sprintf(conn->errorMessage, "PQsendQuery() -- query is too long. "
"Maximum length is %d\n", MAX_MESSAGE_LEN - 2);
return 0;
}
if (conn->asyncStatus != PGASYNC_IDLE) if (conn->asyncStatus != PGASYNC_IDLE)
{ {
sprintf(conn->errorMessage, sprintf(conn->errorMessage,
@ -205,6 +217,26 @@ PQsendQuery(PGconn *conn, const char *query)
return 0; return 0;
} }
/* Check for pending input (asynchronous Notice or Notify messages);
* also detect the case that the backend just closed the connection.
* Note: we have to loop if the first call to pqReadData successfully
* reads some data, since in that case pqReadData won't notice whether
* the connection is now closed.
*/
while (pqReadReady(conn)) {
if (pqReadData(conn) < 0)
return 0; /* errorMessage already set */
parseInput(conn); /* deal with Notice or Notify, if any */
}
/* Don't try to send if we know there's no live connection. */
if (conn->status != CONNECTION_OK)
{
sprintf(conn->errorMessage, "PQsendQuery() -- There is no connection "
"to the backend.\n");
return 0;
}
/* clear the error string */ /* clear the error string */
conn->errorMessage[0] = '\0'; conn->errorMessage[0] = '\0';
@ -213,22 +245,6 @@ PQsendQuery(PGconn *conn, const char *query)
conn->curTuple = NULL; conn->curTuple = NULL;
conn->asyncErrorMessage[0] = '\0'; conn->asyncErrorMessage[0] = '\0';
/* check to see if the query string is too long */
if (strlen(query) > MAX_MESSAGE_LEN-2)
{
sprintf(conn->errorMessage, "PQsendQuery() -- query is too long. "
"Maximum length is %d\n", MAX_MESSAGE_LEN - 2);
return 0;
}
/* Don't try to send if we know there's no live connection. */
if (conn->status != CONNECTION_OK)
{
sprintf(conn->errorMessage, "PQsendQuery() -- There is no connection "
"to the backend.\n");
return 0;
}
/* send the query to the backend; */ /* send the query to the backend; */
/* the frontend-backend protocol uses 'Q' to designate queries */ /* the frontend-backend protocol uses 'Q' to designate queries */
if (pqPutnchar("Q", 1, conn)) if (pqPutnchar("Q", 1, conn))
@ -297,15 +313,19 @@ parseInput(PGconn *conn)
if (pqGetc(&id, conn)) if (pqGetc(&id, conn))
return; return;
/* /*
* NOTIFY messages can happen in any state besides COPY OUT; * NOTIFY and NOTICE messages can happen in any state besides COPY OUT;
* always process them right away. * always process them right away.
*/ */
if (id == 'A') if (id == 'A')
{ {
/* Notify responses can happen at any time */
if (getNotify(conn)) if (getNotify(conn))
return; return;
} }
else if (id == 'N')
{
if (getNotice(conn))
return;
}
else else
{ {
/* /*
@ -318,9 +338,10 @@ parseInput(PGconn *conn)
{ {
if (conn->asyncStatus == PGASYNC_IDLE) if (conn->asyncStatus == PGASYNC_IDLE)
{ {
fprintf(stderr, sprintf(conn->errorMessage,
"Backend message type 0x%02x arrived while idle\n", "Backend message type 0x%02x arrived while idle\n",
id); id);
DONOTICE(conn, conn->errorMessage);
/* Discard the unexpected message; good idea?? */ /* Discard the unexpected message; good idea?? */
conn->inStart = conn->inEnd; conn->inStart = conn->inEnd;
} }
@ -354,8 +375,11 @@ parseInput(PGconn *conn)
if (pqGetc(&id, conn)) if (pqGetc(&id, conn))
return; return;
if (id != '\0') if (id != '\0')
fprintf(stderr, {
sprintf(conn->errorMessage,
"unexpected character %c following 'I'\n", id); "unexpected character %c following 'I'\n", id);
DONOTICE(conn, conn->errorMessage);
}
if (conn->result == NULL) if (conn->result == NULL)
conn->result = makeEmptyPGresult(conn, conn->result = makeEmptyPGresult(conn,
PGRES_EMPTY_QUERY); PGRES_EMPTY_QUERY);
@ -371,10 +395,6 @@ parseInput(PGconn *conn)
if (pqGetInt(&(conn->be_key), 4, conn)) if (pqGetInt(&(conn->be_key), 4, conn))
return; return;
break; break;
case 'N': /* notices from the backend */
if (getNotice(conn))
return;
break;
case 'P': /* synchronous (normal) portal */ case 'P': /* synchronous (normal) portal */
if (pqGets(conn->errorMessage, ERROR_MSG_LENGTH, conn)) if (pqGets(conn->errorMessage, ERROR_MSG_LENGTH, conn))
return; return;
@ -408,8 +428,9 @@ parseInput(PGconn *conn)
} }
else else
{ {
fprintf(stderr, sprintf(conn->errorMessage,
"Backend sent D message without prior T\n"); "Backend sent D message without prior T\n");
DONOTICE(conn, conn->errorMessage);
/* Discard the unexpected message; good idea?? */ /* Discard the unexpected message; good idea?? */
conn->inStart = conn->inEnd; conn->inStart = conn->inEnd;
return; return;
@ -424,8 +445,9 @@ parseInput(PGconn *conn)
} }
else else
{ {
fprintf(stderr, sprintf(conn->errorMessage,
"Backend sent B message without prior T\n"); "Backend sent B message without prior T\n");
DONOTICE(conn, conn->errorMessage);
/* Discard the unexpected message; good idea?? */ /* Discard the unexpected message; good idea?? */
conn->inStart = conn->inEnd; conn->inStart = conn->inEnd;
return; return;
@ -783,14 +805,7 @@ getNotice(PGconn *conn)
{ {
if (pqGets(conn->errorMessage, ERROR_MSG_LENGTH, conn)) if (pqGets(conn->errorMessage, ERROR_MSG_LENGTH, conn))
return EOF; return EOF;
/* DONOTICE(conn, conn->errorMessage);
* Should we really be doing this? These notices
* are not important enough for us to presume to
* put them on stderr. Maybe the caller should
* decide whether to put them on stderr or not.
* BJH 96.12.27
*/
fprintf(stderr, "%s", conn->errorMessage);
return 0; return 0;
} }
@ -970,7 +985,10 @@ PQendcopy(PGconn *conn)
* To recover, reset the connection (talk about using a sledgehammer...) * To recover, reset the connection (talk about using a sledgehammer...)
*/ */
PQclear(result); PQclear(result);
fprintf(stderr, "PQendcopy: resetting connection\n");
sprintf(conn->errorMessage, "PQendcopy: resetting connection\n");
DONOTICE(conn, conn->errorMessage);
PQreset(conn); PQreset(conn);
return 1; return 1;
@ -1156,11 +1174,7 @@ ExecStatusType
PQresultStatus(PGresult *res) PQresultStatus(PGresult *res)
{ {
if (!res) if (!res)
{
fprintf(stderr, "PQresultStatus() -- pointer to PQresult is null\n");
return PGRES_NONFATAL_ERROR; return PGRES_NONFATAL_ERROR;
}
return res->resultStatus; return res->resultStatus;
} }
@ -1168,10 +1182,7 @@ int
PQntuples(PGresult *res) PQntuples(PGresult *res)
{ {
if (!res) if (!res)
{
fprintf(stderr, "PQntuples() -- pointer to PQresult is null\n");
return 0; return 0;
}
return res->ntups; return res->ntups;
} }
@ -1179,32 +1190,64 @@ int
PQnfields(PGresult *res) PQnfields(PGresult *res)
{ {
if (!res) if (!res)
{
fprintf(stderr, "PQnfields() -- pointer to PQresult is null\n");
return 0; return 0;
}
return res->numAttributes; return res->numAttributes;
} }
/*
* Helper routines to range-check field numbers and tuple numbers.
* Return TRUE if OK, FALSE if not
*/
static int
check_field_number(const char *routineName, PGresult *res, int field_num)
{
if (!res)
return FALSE; /* no way to display error message... */
if (field_num < 0 || field_num >= res->numAttributes)
{
sprintf(res->conn->errorMessage,
"%s: ERROR! field number %d is out of range 0..%d\n",
routineName, field_num, res->numAttributes - 1);
DONOTICE(res->conn, res->conn->errorMessage);
return FALSE;
}
return TRUE;
}
static int
check_tuple_field_number(const char *routineName, PGresult *res,
int tup_num, int field_num)
{
if (!res)
return FALSE; /* no way to display error message... */
if (tup_num < 0 || tup_num >= res->ntups)
{
sprintf(res->conn->errorMessage,
"%s: ERROR! tuple number %d is out of range 0..%d\n",
routineName, tup_num, res->ntups - 1);
DONOTICE(res->conn, res->conn->errorMessage);
return FALSE;
}
if (field_num < 0 || field_num >= res->numAttributes)
{
sprintf(res->conn->errorMessage,
"%s: ERROR! field number %d is out of range 0..%d\n",
routineName, field_num, res->numAttributes - 1);
DONOTICE(res->conn, res->conn->errorMessage);
return FALSE;
}
return TRUE;
}
/* /*
returns NULL if the field_num is invalid returns NULL if the field_num is invalid
*/ */
char * char *
PQfname(PGresult *res, int field_num) PQfname(PGresult *res, int field_num)
{ {
if (!res) if (! check_field_number("PQfname", res, field_num))
{
fprintf(stderr, "PQfname() -- pointer to PQresult is null\n");
return NULL; return NULL;
}
if (field_num < 0 || field_num >= res->numAttributes)
{
fprintf(stderr,
"PQfname: ERROR! field number %d is out of range 0..%d\n",
field_num, res->numAttributes - 1);
return NULL;
}
if (res->attDescs) if (res->attDescs)
return res->attDescs[field_num].name; return res->attDescs[field_num].name;
else else
@ -1221,10 +1264,7 @@ PQfnumber(PGresult *res, const char *field_name)
char *field_case; char *field_case;
if (!res) if (!res)
{
fprintf(stderr, "PQfnumber() -- pointer to PQresult is null\n");
return -1; return -1;
}
if (field_name == NULL || if (field_name == NULL ||
field_name[0] == '\0' || field_name[0] == '\0' ||
@ -1258,19 +1298,8 @@ PQfnumber(PGresult *res, const char *field_name)
Oid Oid
PQftype(PGresult *res, int field_num) PQftype(PGresult *res, int field_num)
{ {
if (!res) if (! check_field_number("PQftype", res, field_num))
{
fprintf(stderr, "PQftype() -- pointer to PQresult is null\n");
return InvalidOid; return InvalidOid;
}
if (field_num < 0 || field_num >= res->numAttributes)
{
fprintf(stderr,
"PQftype: ERROR! field number %d is out of range 0..%d\n",
field_num, res->numAttributes - 1);
return InvalidOid;
}
if (res->attDescs) if (res->attDescs)
return res->attDescs[field_num].typid; return res->attDescs[field_num].typid;
else else
@ -1280,19 +1309,8 @@ PQftype(PGresult *res, int field_num)
short short
PQfsize(PGresult *res, int field_num) PQfsize(PGresult *res, int field_num)
{ {
if (!res) if (! check_field_number("PQfsize", res, field_num))
{
fprintf(stderr, "PQfsize() -- pointer to PQresult is null\n");
return 0; return 0;
}
if (field_num < 0 || field_num >= res->numAttributes)
{
fprintf(stderr,
"PQfsize: ERROR! field number %d is out of range 0..%d\n",
field_num, res->numAttributes - 1);
return 0;
}
if (res->attDescs) if (res->attDescs)
return res->attDescs[field_num].typlen; return res->attDescs[field_num].typlen;
else else
@ -1302,19 +1320,8 @@ PQfsize(PGresult *res, int field_num)
int int
PQfmod(PGresult *res, int field_num) PQfmod(PGresult *res, int field_num)
{ {
if (!res) if (! check_field_number("PQfmod", res, field_num))
{
fprintf(stderr, "PQfmod() -- pointer to PQresult is null\n");
return 0; return 0;
}
if (field_num < 0 || field_num >= res->numAttributes)
{
fprintf(stderr,
"PQfmod: ERROR! field number %d is out of range 0..%d\n",
field_num, res->numAttributes - 1);
return 0;
}
if (res->attDescs) if (res->attDescs)
return res->attDescs[field_num].atttypmod; return res->attDescs[field_num].atttypmod;
else else
@ -1325,10 +1332,7 @@ char *
PQcmdStatus(PGresult *res) PQcmdStatus(PGresult *res)
{ {
if (!res) if (!res)
{
fprintf(stderr, "PQcmdStatus() -- pointer to PQresult is null\n");
return NULL; return NULL;
}
return res->cmdStatus; return res->cmdStatus;
} }
@ -1343,10 +1347,7 @@ PQoidStatus(PGresult *res)
static char oidStatus[32] = {0}; static char oidStatus[32] = {0};
if (!res) if (!res)
{ return "";
fprintf(stderr, "PQoidStatus () -- pointer to PQresult is null\n");
return NULL;
}
oidStatus[0] = 0; oidStatus[0] = 0;
@ -1371,10 +1372,7 @@ const char *
PQcmdTuples(PGresult *res) PQcmdTuples(PGresult *res)
{ {
if (!res) if (!res)
{ return "";
fprintf(stderr, "PQcmdTuples () -- pointer to PQresult is null\n");
return NULL;
}
if (strncmp(res->cmdStatus, "INSERT", 6) == 0 || if (strncmp(res->cmdStatus, "INSERT", 6) == 0 ||
strncmp(res->cmdStatus, "DELETE", 6) == 0 || strncmp(res->cmdStatus, "DELETE", 6) == 0 ||
@ -1384,9 +1382,11 @@ PQcmdTuples(PGresult *res)
if (*p == 0) if (*p == 0)
{ {
fprintf(stderr, "PQcmdTuples (%s) -- bad input from server\n", sprintf(res->conn->errorMessage,
"PQcmdTuples (%s) -- bad input from server\n",
res->cmdStatus); res->cmdStatus);
return NULL; DONOTICE(res->conn, res->conn->errorMessage);
return "";
} }
p++; p++;
if (*(res->cmdStatus) != 'I') /* UPDATE/DELETE */ if (*(res->cmdStatus) != 'I') /* UPDATE/DELETE */
@ -1395,8 +1395,10 @@ PQcmdTuples(PGresult *res)
p++; /* INSERT: skip oid */ p++; /* INSERT: skip oid */
if (*p == 0) if (*p == 0)
{ {
fprintf(stderr, "PQcmdTuples (INSERT) -- there's no # of tuples\n"); sprintf(res->conn->errorMessage,
return NULL; "PQcmdTuples (INSERT) -- there's no # of tuples\n");
DONOTICE(res->conn, res->conn->errorMessage);
return "";
} }
p++; p++;
return (p); return (p);
@ -1417,28 +1419,8 @@ PQcmdTuples(PGresult *res)
char * char *
PQgetvalue(PGresult *res, int tup_num, int field_num) PQgetvalue(PGresult *res, int tup_num, int field_num)
{ {
if (!res) if (! check_tuple_field_number("PQgetvalue", res, tup_num, field_num))
{
fprintf(stderr, "PQgetvalue: pointer to PQresult is null\n");
return NULL; return NULL;
}
if (tup_num < 0 || tup_num >= res->ntups)
{
fprintf(stderr,
"PQgetvalue: There is no row %d in the query results. "
"The highest numbered row is %d.\n",
tup_num, res->ntups - 1);
return NULL;
}
if (field_num < 0 || field_num >= res->numAttributes)
{
fprintf(stderr,
"PQgetvalue: There is no field %d in the query results. "
"The highest numbered field is %d.\n",
field_num, res->numAttributes - 1);
return NULL;
}
return res->tuples[tup_num][field_num].value; return res->tuples[tup_num][field_num].value;
} }
@ -1450,29 +1432,8 @@ PQgetvalue(PGresult *res, int tup_num, int field_num)
int int
PQgetlength(PGresult *res, int tup_num, int field_num) PQgetlength(PGresult *res, int tup_num, int field_num)
{ {
if (!res) if (! check_tuple_field_number("PQgetlength", res, tup_num, field_num))
{
fprintf(stderr, "PQgetlength() -- pointer to PQresult is null\n");
return 0; return 0;
}
if (tup_num < 0 || tup_num >= res->ntups)
{
fprintf(stderr,
"PQgetlength: There is no row %d in the query results. "
"The highest numbered row is %d.\n",
tup_num, res->ntups - 1);
return 0;
}
if (field_num < 0 || field_num >= res->numAttributes)
{
fprintf(stderr,
"PQgetlength: There is no field %d in the query results. "
"The highest numbered field is %d.\n",
field_num, res->numAttributes - 1);
return 0;
}
if (res->tuples[tup_num][field_num].len != NULL_LEN) if (res->tuples[tup_num][field_num].len != NULL_LEN)
return res->tuples[tup_num][field_num].len; return res->tuples[tup_num][field_num].len;
else else
@ -1485,28 +1446,8 @@ PQgetlength(PGresult *res, int tup_num, int field_num)
int int
PQgetisnull(PGresult *res, int tup_num, int field_num) PQgetisnull(PGresult *res, int tup_num, int field_num)
{ {
if (!res) if (! check_tuple_field_number("PQgetisnull", res, tup_num, field_num))
{
fprintf(stderr, "PQgetisnull() -- pointer to PQresult is null\n");
return 1; /* pretend it is null */ return 1; /* pretend it is null */
}
if (tup_num < 0 || tup_num >= res->ntups)
{
fprintf(stderr,
"PQgetisnull: There is no row %d in the query results. "
"The highest numbered row is %d.\n",
tup_num, res->ntups - 1);
return 1; /* pretend it is null */
}
if (field_num < 0 || field_num >= res->numAttributes)
{
fprintf(stderr,
"PQgetisnull: There is no field %d in the query results. "
"The highest numbered field is %d.\n",
field_num, res->numAttributes - 1);
return 1; /* pretend it is null */
}
if (res->tuples[tup_num][field_num].len == NULL_LEN) if (res->tuples[tup_num][field_num].len == NULL_LEN)
return 1; return 1;
else else

View File

@ -24,7 +24,7 @@
* *
* *
* IDENTIFICATION * 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 "postgres.h"
#include "libpq-fe.h" #include "libpq-fe.h"
#define DONOTICE(conn,message) \
((*(conn)->noticeHook) ((conn)->noticeArg, (message)))
/* --------------------------------------------------------------------- */ /* --------------------------------------------------------------------- */
/* pqGetc: /* pqGetc:
get a character from the connection get a character from the connection
@ -218,7 +222,9 @@ pqGetInt(int *result, int bytes, PGconn *conn)
*result = (int) ntohl(tmp4); *result = (int) ntohl(tmp4);
break; break;
default: 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; return EOF;
} }
@ -252,7 +258,9 @@ pqPutInt(int value, int bytes, PGconn *conn)
return EOF; return EOF;
break; break;
default: 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; return EOF;
} }
@ -265,7 +273,7 @@ pqPutInt(int value, int bytes, PGconn *conn)
/* --------------------------------------------------------------------- */ /* --------------------------------------------------------------------- */
/* pqReadReady: is select() saying the file is ready to read? /* pqReadReady: is select() saying the file is ready to read?
*/ */
static int int
pqReadReady(PGconn *conn) pqReadReady(PGconn *conn)
{ {
fd_set input_mask; fd_set input_mask;

View File

@ -9,7 +9,7 @@
* didn't really belong there. * didn't really belong there.
* *
* IDENTIFICATION * 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 fs_len = strlen(po->fieldSep);
int total_line_length = 0; int total_line_length = 0;
int usePipe = 0; int usePipe = 0;
pqsigfunc oldsigpipehandler = NULL;
char *pagerenv; char *pagerenv;
char buf[8192 * 2 + 1]; char buf[8192 * 2 + 1];
@ -193,7 +194,7 @@ PQprint(FILE *fout,
{ {
usePipe = 1; usePipe = 1;
#ifndef WIN32 #ifndef WIN32
pqsignal(SIGPIPE, SIG_IGN); oldsigpipehandler = pqsignal(SIGPIPE, SIG_IGN);
#endif #endif
} }
else else
@ -309,7 +310,7 @@ PQprint(FILE *fout,
_pclose(fout); _pclose(fout);
#else #else
pclose(fout); pclose(fout);
pqsignal(SIGPIPE, SIG_DFL); pqsignal(SIGPIPE, oldsigpipehandler);
#endif #endif
} }
if (po->html3 && !po->expanded) if (po->html3 && !po->expanded)

View File

@ -6,7 +6,7 @@
* *
* Copyright (c) 1994, Regents of the University of California * 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 */ int be_pid; /* process id of backend */
} PGnotify; } 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 */ /* PGAsyncStatusType is private to libpq, really shouldn't be seen by users */
typedef enum typedef enum
{ {
@ -165,6 +168,10 @@ extern "C"
/* Optional file to write trace info to */ /* Optional file to write trace info to */
FILE *Pfdebug; FILE *Pfdebug;
/* Callback procedure for notice/error message processing */
PQnoticeProcessor noticeHook;
void *noticeArg;
/* Status indicators */ /* Status indicators */
ConnStatusType status; ConnStatusType status;
PGAsyncStatusType asyncStatus; PGAsyncStatusType asyncStatus;
@ -300,6 +307,11 @@ extern "C"
extern void PQtrace(PGconn *conn, FILE *debug_port); extern void PQtrace(PGconn *conn, FILE *debug_port);
extern void PQuntrace(PGconn *conn); extern void PQuntrace(PGconn *conn);
/* Override default notice processor */
extern void PQsetNoticeProcessor (PGconn *conn,
PQnoticeProcessor proc,
void *arg);
/* === in fe-exec.c === */ /* === in fe-exec.c === */
/* Simple synchronous query */ /* Simple synchronous query */
extern PGresult *PQexec(PGconn *conn, const char *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 pqGetInt(int *result, int bytes, PGconn *conn);
extern int pqPutInt(int value, int bytes, PGconn *conn); extern int pqPutInt(int value, int bytes, PGconn *conn);
extern int pqReadData(PGconn *conn); extern int pqReadData(PGconn *conn);
extern int pqReadReady(PGconn *conn);
extern int pqFlush(PGconn *conn); extern int pqFlush(PGconn *conn);
extern int pqWait(int forRead, int forWrite, PGconn *conn); extern int pqWait(int forRead, int forWrite, PGconn *conn);

View File

@ -1,7 +1,7 @@
.\" This is -*-nroff-*- .\" This is -*-nroff-*-
.\" XXX standard disclaimer belongs here.... .\" XXX standard disclaimer belongs here....
.\" $Header: /cvsroot/pgsql/src/man/Attic/libpq.3,v 1.22 1998/07/15 17:34:06 momjian Exp $ .\" $Header: /cvsroot/pgsql/src/man/Attic/libpq.3,v 1.23 1998/08/09 02:59:33 momjian Exp $
.TH LIBPQ INTRO 07/08/98 PostgreSQL PostgreSQL .TH LIBPQ INTRO 08/08/98 PostgreSQL PostgreSQL
.SH DESCRIPTION .SH DESCRIPTION
Libpq is the programmer's interface to Postgres. Libpq is a set of Libpq is the programmer's interface to Postgres. Libpq is a set of
library routines which allows library routines which allows
@ -823,6 +823,36 @@ Disable tracing started by
.nf .nf
void PQuntrace(PGconn *conn) void PQuntrace(PGconn *conn)
.fi .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 .PP
.SH "User Authentication Functions" .SH "User Authentication Functions"
.PP .PP