1
0
mirror of https://github.com/postgres/postgres.git synced 2025-08-27 07:42:10 +03:00

Infrastructure for upgraded error reporting mechanism. elog.c is

rewritten and the protocol is changed, but most elog calls are still
elog calls.  Also, we need to contemplate mechanisms for controlling
all this functionality --- eg, how much stuff should appear in the
postmaster log?  And what API should libpq expose for it?
This commit is contained in:
Tom Lane
2003-04-24 21:16:45 +00:00
parent a91c5be6a4
commit f690920a75
33 changed files with 1713 additions and 728 deletions

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.234 2003/04/22 00:08:07 tgl Exp $
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.235 2003/04/24 21:16:44 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -1429,20 +1429,13 @@ keep_going: /* We will come back to here until there
/* Handle errors. */
if (beresp == 'E')
{
if (pqGets(&conn->errorMessage, conn))
if (pqGetErrorNotice(conn, true))
{
/* We'll come back when there is more data */
return PGRES_POLLING_READING;
}
/* OK, we read the message; mark data consumed */
conn->inStart = conn->inCursor;
/*
* The postmaster typically won't end its message with
* a newline, so add one to conform to libpq
* conventions.
*/
appendPQExpBufferChar(&conn->errorMessage, '\n');
goto error_return;
}

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.130 2003/04/22 00:08:07 tgl Exp $
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.131 2003/04/24 21:16:44 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -55,7 +55,6 @@ static void handleSyncLoss(PGconn *conn, char id, int msgLength);
static int getRowDescriptions(PGconn *conn);
static int getAnotherTuple(PGconn *conn, int binary);
static int getNotify(PGconn *conn);
static int getNotice(PGconn *conn);
/* ---------------
* Escaping arbitrary strings to get valid SQL strings/identifiers.
@@ -390,6 +389,16 @@ PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status)
result->cmdStatus[0] = '\0';
result->binary = 0;
result->errMsg = NULL;
result->errSeverity = NULL;
result->errCode = NULL;
result->errPrimary = NULL;
result->errDetail = NULL;
result->errHint = NULL;
result->errPosition = NULL;
result->errContext = NULL;
result->errFilename = NULL;
result->errLineno = NULL;
result->errFuncname = NULL;
result->null_field[0] = '\0';
result->curBlock = NULL;
result->curOffset = 0;
@@ -949,7 +958,7 @@ parseInput(PGconn *conn)
}
else if (id == 'N')
{
if (getNotice(conn))
if (pqGetErrorNotice(conn, false))
return;
}
else if (conn->asyncStatus != PGASYNC_BUSY)
@@ -968,7 +977,7 @@ parseInput(PGconn *conn)
*/
if (id == 'E')
{
if (getNotice(conn))
if (pqGetErrorNotice(conn, false /* treat as notice */))
return;
}
else
@@ -999,10 +1008,8 @@ parseInput(PGconn *conn)
conn->asyncStatus = PGASYNC_READY;
break;
case 'E': /* error return */
if (pqGets(&conn->errorMessage, conn))
if (pqGetErrorNotice(conn, true))
return;
/* build an error result holding the error message */
saveErrorResult(conn);
conn->asyncStatus = PGASYNC_READY;
break;
case 'Z': /* backend is ready for new query */
@@ -1540,31 +1547,132 @@ errout:
/*
* Attempt to read a Notice response message.
* Attempt to read an Error or Notice response message.
* This is possible in several places, so we break it out as a subroutine.
* Entry: 'N' message type and length have already been consumed.
* Exit: returns 0 if successfully consumed Notice message.
* Entry: 'E' or 'N' message type and length have already been consumed.
* Exit: returns 0 if successfully consumed message.
* returns EOF if not enough data.
*/
static int
getNotice(PGconn *conn)
int
pqGetErrorNotice(PGconn *conn, bool isError)
{
/*
* Since the Notice might be pretty long, we create a temporary
* PQExpBuffer rather than using conn->workBuffer. workBuffer is
* intended for stuff that is expected to be short.
*/
PQExpBufferData noticeBuf;
PGresult *res;
PQExpBufferData workBuf;
char id;
initPQExpBuffer(&noticeBuf);
if (pqGets(&noticeBuf, conn))
/*
* Make a PGresult to hold the accumulated fields. We temporarily
* lie about the result status, so that PQmakeEmptyPGresult doesn't
* uselessly copy conn->errorMessage.
*/
res = PQmakeEmptyPGresult(conn, PGRES_EMPTY_QUERY);
res->resultStatus = PGRES_FATAL_ERROR;
/*
* Since the fields might be pretty long, we create a temporary
* PQExpBuffer rather than using conn->workBuffer. workBuffer is
* intended for stuff that is expected to be short. We shouldn't
* use conn->errorMessage either, since this might be only a notice.
*/
initPQExpBuffer(&workBuf);
/*
* Read the fields and save into res.
*/
for (;;)
{
termPQExpBuffer(&noticeBuf);
return EOF;
if (pqGetc(&id, conn))
goto fail;
if (id == '\0')
break; /* terminator found */
if (pqGets(&workBuf, conn))
goto fail;
switch (id)
{
case 'S':
res->errSeverity = pqResultStrdup(res, workBuf.data);
break;
case 'C':
res->errCode = pqResultStrdup(res, workBuf.data);
break;
case 'M':
res->errPrimary = pqResultStrdup(res, workBuf.data);
break;
case 'D':
res->errDetail = pqResultStrdup(res, workBuf.data);
break;
case 'H':
res->errHint = pqResultStrdup(res, workBuf.data);
break;
case 'P':
res->errPosition = pqResultStrdup(res, workBuf.data);
break;
case 'W':
res->errContext = pqResultStrdup(res, workBuf.data);
break;
case 'F':
res->errFilename = pqResultStrdup(res, workBuf.data);
break;
case 'L':
res->errLineno = pqResultStrdup(res, workBuf.data);
break;
case 'R':
res->errFuncname = pqResultStrdup(res, workBuf.data);
break;
default:
/* silently ignore any other field type */
break;
}
}
DONOTICE(conn, noticeBuf.data);
termPQExpBuffer(&noticeBuf);
/*
* Now build the "overall" error message for PQresultErrorMessage.
*
* XXX this should be configurable somehow.
*/
resetPQExpBuffer(&workBuf);
if (res->errSeverity)
appendPQExpBuffer(&workBuf, "%s: ", res->errSeverity);
if (res->errPrimary)
appendPQExpBufferStr(&workBuf, res->errPrimary);
/* translator: %s represents a digit string */
if (res->errPosition)
appendPQExpBuffer(&workBuf, libpq_gettext(" at character %s"),
res->errPosition);
appendPQExpBufferChar(&workBuf, '\n');
if (res->errDetail)
appendPQExpBuffer(&workBuf, libpq_gettext("DETAIL: %s\n"),
res->errDetail);
if (res->errHint)
appendPQExpBuffer(&workBuf, libpq_gettext("HINT: %s\n"),
res->errHint);
if (res->errContext)
appendPQExpBuffer(&workBuf, libpq_gettext("CONTEXT: %s\n"),
res->errContext);
/*
* Either save error as current async result, or just emit the notice.
*/
if (isError)
{
res->errMsg = pqResultStrdup(res, workBuf.data);
pqClearAsyncResult(conn);
conn->result = res;
resetPQExpBuffer(&conn->errorMessage);
appendPQExpBufferStr(&conn->errorMessage, workBuf.data);
}
else
{
DONOTICE(conn, workBuf.data);
PQclear(res);
}
termPQExpBuffer(&workBuf);
return 0;
fail:
PQclear(res);
termPQExpBuffer(&workBuf);
return EOF;
}
/*
@@ -2123,10 +2231,8 @@ PQfn(PGconn *conn,
}
break;
case 'E': /* error return */
if (pqGets(&conn->errorMessage, conn))
if (pqGetErrorNotice(conn, true))
continue;
/* build an error result holding the error message */
saveErrorResult(conn);
status = PGRES_FATAL_ERROR;
break;
case 'A': /* notify message */
@@ -2136,7 +2242,7 @@ PQfn(PGconn *conn,
break;
case 'N': /* notice */
/* handle notice and go back to processing return values */
if (getNotice(conn))
if (pqGetErrorNotice(conn, false))
continue;
break;
case 'Z': /* backend is ready for new query */

View File

@@ -12,7 +12,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: libpq-int.h,v 1.63 2003/04/22 00:08:07 tgl Exp $
* $Id: libpq-int.h,v 1.64 2003/04/24 21:16:44 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -56,7 +56,7 @@ typedef int ssize_t; /* ssize_t doesn't exist in VC (atleast
* pqcomm.h describe what the backend knows, not what libpq knows.
*/
#define PG_PROTOCOL_LIBPQ PG_PROTOCOL(3,102) /* XXX temporary value */
#define PG_PROTOCOL_LIBPQ PG_PROTOCOL(3,103) /* XXX temporary value */
/*
* POSTGRES backend dependent Constants.
@@ -101,7 +101,7 @@ typedef struct pgresAttDesc
/* We use char* for Attribute values.
The value pointer always points to a null-terminated area; we add a
null (zero) byte after whatever the backend sends us. This is only
particularly useful for ASCII tuples ... with a binary value, the
particularly useful for text tuples ... with a binary value, the
value might have embedded nulls, so the application can't use C string
operators on it. But we add a null anyway for consistency.
Note that the value itself does not contain a length word.
@@ -133,7 +133,7 @@ struct pg_result
char cmdStatus[CMDSTATUS_LEN]; /* cmd status from the
* last query */
int binary; /* binary tuple values if binary == 1,
* otherwise ASCII */
* otherwise text */
/*
* The conn link in PGresult is no longer used by any libpq code. It
@@ -152,9 +152,25 @@ struct pg_result
void *noticeArg;
int client_encoding; /* encoding id */
/*
* Error information (all NULL if not an error result). errMsg is the
* "overall" error message returned by PQresultErrorMessage. If we
* got a field-ized error from the server then the additional fields
* may be set.
*/
char *errMsg; /* error message, or NULL if no error */
char *errSeverity; /* severity code */
char *errCode; /* SQLSTATE code */
char *errPrimary; /* primary message text */
char *errDetail; /* detail text */
char *errHint; /* hint text */
char *errPosition; /* cursor position */
char *errContext; /* location information */
char *errFilename; /* source-code file name */
char *errLineno; /* source-code line number */
char *errFuncname; /* source-code function name */
/* All NULL attributes in the query result point to this null string */
char null_field[1];
@@ -321,6 +337,7 @@ extern void pqSetResultError(PGresult *res, const char *msg);
extern void *pqResultAlloc(PGresult *res, size_t nBytes, bool isBinary);
extern char *pqResultStrdup(PGresult *res, const char *str);
extern void pqClearAsyncResult(PGconn *conn);
extern int pqGetErrorNotice(PGconn *conn, bool isError);
/* === in fe-misc.c === */