diff options
Diffstat (limited to 'src/interfaces/libpq/fe-exec.c')
-rw-r--r-- | src/interfaces/libpq/fe-exec.c | 156 |
1 files changed, 131 insertions, 25 deletions
diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c index 16e63f7f68f..3fe0ddc4920 100644 --- a/src/interfaces/libpq/fe-exec.c +++ b/src/interfaces/libpq/fe-exec.c @@ -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) { + PGresult *res; + PQExpBufferData workBuf; + char id; + + /* + * 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 Notice might be pretty long, we create a temporary + * 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. + * intended for stuff that is expected to be short. We shouldn't + * use conn->errorMessage either, since this might be only a notice. */ - PQExpBufferData noticeBuf; + initPQExpBuffer(&workBuf); - initPQExpBuffer(¬iceBuf); - if (pqGets(¬iceBuf, conn)) + /* + * Read the fields and save into res. + */ + for (;;) { - termPQExpBuffer(¬iceBuf); - 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(¬iceBuf); + + /* + * 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 */ |