summaryrefslogtreecommitdiff
path: root/src/interfaces
diff options
context:
space:
mode:
Diffstat (limited to 'src/interfaces')
-rw-r--r--src/interfaces/libpq/fe-connect.c11
-rw-r--r--src/interfaces/libpq/fe-exec.c156
-rw-r--r--src/interfaces/libpq/libpq-int.h27
3 files changed, 155 insertions, 39 deletions
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 086462094f..6ee2716ec0 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -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;
}
diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c
index 16e63f7f68..3fe0ddc492 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(&noticeBuf);
- if (pqGets(&noticeBuf, conn))
+ /*
+ * 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 */
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 35e3208eb0..4688fbf8f5 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -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 === */