Add libpq support for recreating an error message with different verbosity.
Often, upon getting an unexpected error in psql, one's first wish is that the verbosity setting had been higher; for example, to be able to see the schema-name field or the server code location info. Up to now the only way has been to adjust the VERBOSITY variable and repeat the failing query. That's a pain, and it doesn't work if the error isn't reproducible. This commit adds support in libpq for regenerating the error message for an existing error PGresult at any desired verbosity level. This is almost just a matter of refactoring the existing code into a subroutine, but there is one bit of possibly-needed information that was not getting put into PGresults: the text of the last query sent to the server. We must add that string to the contents of an error PGresult. But we only need to save it if it might be used, which with the existing error-formatting code only happens if there is a PG_DIAG_STATEMENT_POSITION error field, which is probably pretty rare for errors in production situations. So really the overhead when the feature isn't used should be negligible. Alex Shulgin, reviewed by Daniel Vérité, some improvements by me
This commit is contained in:
parent
5a5b917184
commit
e3161b231c
@ -2691,6 +2691,48 @@ char *PQresultErrorMessage(const PGresult *res);
|
|||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry id="libpq-pqresultverboseerrormessage">
|
||||||
|
<term>
|
||||||
|
<function>PQresultVerboseErrorMessage</function>
|
||||||
|
<indexterm>
|
||||||
|
<primary>PQresultVerboseErrorMessage</primary>
|
||||||
|
</indexterm>
|
||||||
|
</term>
|
||||||
|
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Returns a reformatted version of the error message associated with
|
||||||
|
a <structname>PGresult</> object.
|
||||||
|
<synopsis>
|
||||||
|
char *PQresultVerboseErrorMessage(const PGresult *res,
|
||||||
|
PGVerbosity verbosity,
|
||||||
|
PGContextVisibility show_context);
|
||||||
|
</synopsis>
|
||||||
|
In some situations a client might wish to obtain a more detailed
|
||||||
|
version of a previously-reported error.
|
||||||
|
<function>PQresultVerboseErrorMessage</function> addresses this need
|
||||||
|
by computing the message that would have been produced
|
||||||
|
by <function>PQresultErrorMessage</function> if the specified
|
||||||
|
verbosity settings had been in effect for the connection when the
|
||||||
|
given <structname>PGresult</> was generated. If
|
||||||
|
the <structname>PGresult</> is not an error result,
|
||||||
|
<quote>PGresult is not an error result</> is reported instead.
|
||||||
|
The returned string includes a trailing newline.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Unlike most other functions for extracting data from
|
||||||
|
a <structname>PGresult</>, the result of this function is a freshly
|
||||||
|
allocated string. The caller must free it
|
||||||
|
using <function>PQfreemem()</> when the string is no longer needed.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
A NULL return is possible if there is insufficient memory.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
<varlistentry id="libpq-pqresulterrorfield">
|
<varlistentry id="libpq-pqresulterrorfield">
|
||||||
<term><function>PQresultErrorField</function><indexterm><primary>PQresultErrorField</></></term>
|
<term><function>PQresultErrorField</function><indexterm><primary>PQresultErrorField</></></term>
|
||||||
<listitem>
|
<listitem>
|
||||||
@ -5582,6 +5624,8 @@ PGVerbosity PQsetErrorVerbosity(PGconn *conn, PGVerbosity verbosity);
|
|||||||
mode includes all available fields. Changing the verbosity does not
|
mode includes all available fields. Changing the verbosity does not
|
||||||
affect the messages available from already-existing
|
affect the messages available from already-existing
|
||||||
<structname>PGresult</> objects, only subsequently-created ones.
|
<structname>PGresult</> objects, only subsequently-created ones.
|
||||||
|
(But see <function>PQresultVerboseErrorMessage</function> if you
|
||||||
|
want to print a previous error with a different verbosity.)
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
@ -5622,6 +5666,8 @@ PGContextVisibility PQsetErrorContextVisibility(PGconn *conn, PGContextVisibilit
|
|||||||
affect the messages available from
|
affect the messages available from
|
||||||
already-existing <structname>PGresult</> objects, only
|
already-existing <structname>PGresult</> objects, only
|
||||||
subsequently-created ones.
|
subsequently-created ones.
|
||||||
|
(But see <function>PQresultVerboseErrorMessage</function> if you
|
||||||
|
want to print a previous error with a different display mode.)
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
@ -6089,8 +6135,9 @@ PQsetNoticeProcessor(PGconn *conn,
|
|||||||
receiver function is called. It is passed the message in the form of
|
receiver function is called. It is passed the message in the form of
|
||||||
a <symbol>PGRES_NONFATAL_ERROR</symbol>
|
a <symbol>PGRES_NONFATAL_ERROR</symbol>
|
||||||
<structname>PGresult</structname>. (This allows the receiver to extract
|
<structname>PGresult</structname>. (This allows the receiver to extract
|
||||||
individual fields using <function>PQresultErrorField</>, or the complete
|
individual fields using <function>PQresultErrorField</>, or obtain a
|
||||||
preformatted message using <function>PQresultErrorMessage</>.) The same
|
complete preformatted message using <function>PQresultErrorMessage</>
|
||||||
|
or <function>PQresultVerboseErrorMessage</>.) The same
|
||||||
void pointer passed to <function>PQsetNoticeReceiver</function> is also
|
void pointer passed to <function>PQsetNoticeReceiver</function> is also
|
||||||
passed. (This pointer can be used to access application-specific state
|
passed. (This pointer can be used to access application-specific state
|
||||||
if needed.)
|
if needed.)
|
||||||
|
@ -170,3 +170,4 @@ PQsslStruct 167
|
|||||||
PQsslAttributeNames 168
|
PQsslAttributeNames 168
|
||||||
PQsslAttribute 169
|
PQsslAttribute 169
|
||||||
PQsetErrorContextVisibility 170
|
PQsetErrorContextVisibility 170
|
||||||
|
PQresultVerboseErrorMessage 171
|
||||||
|
@ -159,6 +159,7 @@ PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status)
|
|||||||
result->nEvents = 0;
|
result->nEvents = 0;
|
||||||
result->errMsg = NULL;
|
result->errMsg = NULL;
|
||||||
result->errFields = NULL;
|
result->errFields = NULL;
|
||||||
|
result->errQuery = NULL;
|
||||||
result->null_field[0] = '\0';
|
result->null_field[0] = '\0';
|
||||||
result->curBlock = NULL;
|
result->curBlock = NULL;
|
||||||
result->curOffset = 0;
|
result->curOffset = 0;
|
||||||
@ -2598,6 +2599,44 @@ PQresultErrorMessage(const PGresult *res)
|
|||||||
return res->errMsg;
|
return res->errMsg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char *
|
||||||
|
PQresultVerboseErrorMessage(const PGresult *res,
|
||||||
|
PGVerbosity verbosity,
|
||||||
|
PGContextVisibility show_context)
|
||||||
|
{
|
||||||
|
PQExpBufferData workBuf;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Because the caller is expected to free the result string, we must
|
||||||
|
* strdup any constant result. We use plain strdup and document that
|
||||||
|
* callers should expect NULL if out-of-memory.
|
||||||
|
*/
|
||||||
|
if (!res ||
|
||||||
|
(res->resultStatus != PGRES_FATAL_ERROR &&
|
||||||
|
res->resultStatus != PGRES_NONFATAL_ERROR))
|
||||||
|
return strdup(libpq_gettext("PGresult is not an error result\n"));
|
||||||
|
|
||||||
|
initPQExpBuffer(&workBuf);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Currently, we pass this off to fe-protocol3.c in all cases; it will
|
||||||
|
* behave reasonably sanely with an error reported by fe-protocol2.c as
|
||||||
|
* well. If necessary, we could record the protocol version in PGresults
|
||||||
|
* so as to be able to invoke a version-specific message formatter, but
|
||||||
|
* for now there's no need.
|
||||||
|
*/
|
||||||
|
pqBuildErrorMessage3(&workBuf, res, verbosity, show_context);
|
||||||
|
|
||||||
|
/* If insufficient memory to format the message, fail cleanly */
|
||||||
|
if (PQExpBufferDataBroken(workBuf))
|
||||||
|
{
|
||||||
|
termPQExpBuffer(&workBuf);
|
||||||
|
return strdup(libpq_gettext("out of memory\n"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return workBuf.data;
|
||||||
|
}
|
||||||
|
|
||||||
char *
|
char *
|
||||||
PQresultErrorField(const PGresult *res, int fieldcode)
|
PQresultErrorField(const PGresult *res, int fieldcode)
|
||||||
{
|
{
|
||||||
|
@ -876,11 +876,9 @@ int
|
|||||||
pqGetErrorNotice3(PGconn *conn, bool isError)
|
pqGetErrorNotice3(PGconn *conn, bool isError)
|
||||||
{
|
{
|
||||||
PGresult *res = NULL;
|
PGresult *res = NULL;
|
||||||
|
bool have_position = false;
|
||||||
PQExpBufferData workBuf;
|
PQExpBufferData workBuf;
|
||||||
char id;
|
char id;
|
||||||
const char *val;
|
|
||||||
const char *querytext = NULL;
|
|
||||||
int querypos = 0;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Since the fields might be pretty long, we create a temporary
|
* Since the fields might be pretty long, we create a temporary
|
||||||
@ -905,6 +903,9 @@ pqGetErrorNotice3(PGconn *conn, bool isError)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Read the fields and save into res.
|
* Read the fields and save into res.
|
||||||
|
*
|
||||||
|
* While at it, save the SQLSTATE in conn->last_sqlstate, and note whether
|
||||||
|
* we saw a PG_DIAG_STATEMENT_POSITION field.
|
||||||
*/
|
*/
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
@ -915,131 +916,26 @@ pqGetErrorNotice3(PGconn *conn, bool isError)
|
|||||||
if (pqGets(&workBuf, conn))
|
if (pqGets(&workBuf, conn))
|
||||||
goto fail;
|
goto fail;
|
||||||
pqSaveMessageField(res, id, workBuf.data);
|
pqSaveMessageField(res, id, workBuf.data);
|
||||||
|
if (id == PG_DIAG_SQLSTATE)
|
||||||
|
strlcpy(conn->last_sqlstate, workBuf.data,
|
||||||
|
sizeof(conn->last_sqlstate));
|
||||||
|
else if (id == PG_DIAG_STATEMENT_POSITION)
|
||||||
|
have_position = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Save the active query text, if any, into res as well; but only if we
|
||||||
|
* might need it for an error cursor display, which is only true if there
|
||||||
|
* is a PG_DIAG_STATEMENT_POSITION field.
|
||||||
|
*/
|
||||||
|
if (have_position && conn->last_query && res)
|
||||||
|
res->errQuery = pqResultStrdup(res, conn->last_query);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Now build the "overall" error message for PQresultErrorMessage.
|
* Now build the "overall" error message for PQresultErrorMessage.
|
||||||
*
|
|
||||||
* Also, save the SQLSTATE in conn->last_sqlstate.
|
|
||||||
*/
|
*/
|
||||||
resetPQExpBuffer(&workBuf);
|
resetPQExpBuffer(&workBuf);
|
||||||
val = PQresultErrorField(res, PG_DIAG_SEVERITY);
|
pqBuildErrorMessage3(&workBuf, res, conn->verbosity, conn->show_context);
|
||||||
if (val)
|
|
||||||
appendPQExpBuffer(&workBuf, "%s: ", val);
|
|
||||||
val = PQresultErrorField(res, PG_DIAG_SQLSTATE);
|
|
||||||
if (val)
|
|
||||||
{
|
|
||||||
if (strlen(val) < sizeof(conn->last_sqlstate))
|
|
||||||
strcpy(conn->last_sqlstate, val);
|
|
||||||
if (conn->verbosity == PQERRORS_VERBOSE)
|
|
||||||
appendPQExpBuffer(&workBuf, "%s: ", val);
|
|
||||||
}
|
|
||||||
val = PQresultErrorField(res, PG_DIAG_MESSAGE_PRIMARY);
|
|
||||||
if (val)
|
|
||||||
appendPQExpBufferStr(&workBuf, val);
|
|
||||||
val = PQresultErrorField(res, PG_DIAG_STATEMENT_POSITION);
|
|
||||||
if (val)
|
|
||||||
{
|
|
||||||
if (conn->verbosity != PQERRORS_TERSE && conn->last_query != NULL)
|
|
||||||
{
|
|
||||||
/* emit position as a syntax cursor display */
|
|
||||||
querytext = conn->last_query;
|
|
||||||
querypos = atoi(val);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* emit position as text addition to primary message */
|
|
||||||
/* translator: %s represents a digit string */
|
|
||||||
appendPQExpBuffer(&workBuf, libpq_gettext(" at character %s"),
|
|
||||||
val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
val = PQresultErrorField(res, PG_DIAG_INTERNAL_POSITION);
|
|
||||||
if (val)
|
|
||||||
{
|
|
||||||
querytext = PQresultErrorField(res, PG_DIAG_INTERNAL_QUERY);
|
|
||||||
if (conn->verbosity != PQERRORS_TERSE && querytext != NULL)
|
|
||||||
{
|
|
||||||
/* emit position as a syntax cursor display */
|
|
||||||
querypos = atoi(val);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* emit position as text addition to primary message */
|
|
||||||
/* translator: %s represents a digit string */
|
|
||||||
appendPQExpBuffer(&workBuf, libpq_gettext(" at character %s"),
|
|
||||||
val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
appendPQExpBufferChar(&workBuf, '\n');
|
|
||||||
if (conn->verbosity != PQERRORS_TERSE)
|
|
||||||
{
|
|
||||||
if (querytext && querypos > 0)
|
|
||||||
reportErrorPosition(&workBuf, querytext, querypos,
|
|
||||||
conn->client_encoding);
|
|
||||||
val = PQresultErrorField(res, PG_DIAG_MESSAGE_DETAIL);
|
|
||||||
if (val)
|
|
||||||
appendPQExpBuffer(&workBuf, libpq_gettext("DETAIL: %s\n"), val);
|
|
||||||
val = PQresultErrorField(res, PG_DIAG_MESSAGE_HINT);
|
|
||||||
if (val)
|
|
||||||
appendPQExpBuffer(&workBuf, libpq_gettext("HINT: %s\n"), val);
|
|
||||||
val = PQresultErrorField(res, PG_DIAG_INTERNAL_QUERY);
|
|
||||||
if (val)
|
|
||||||
appendPQExpBuffer(&workBuf, libpq_gettext("QUERY: %s\n"), val);
|
|
||||||
if (conn->show_context == PQSHOW_CONTEXT_ALWAYS ||
|
|
||||||
(conn->show_context == PQSHOW_CONTEXT_ERRORS && isError))
|
|
||||||
{
|
|
||||||
val = PQresultErrorField(res, PG_DIAG_CONTEXT);
|
|
||||||
if (val)
|
|
||||||
appendPQExpBuffer(&workBuf, libpq_gettext("CONTEXT: %s\n"),
|
|
||||||
val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (conn->verbosity == PQERRORS_VERBOSE)
|
|
||||||
{
|
|
||||||
val = PQresultErrorField(res, PG_DIAG_SCHEMA_NAME);
|
|
||||||
if (val)
|
|
||||||
appendPQExpBuffer(&workBuf,
|
|
||||||
libpq_gettext("SCHEMA NAME: %s\n"), val);
|
|
||||||
val = PQresultErrorField(res, PG_DIAG_TABLE_NAME);
|
|
||||||
if (val)
|
|
||||||
appendPQExpBuffer(&workBuf,
|
|
||||||
libpq_gettext("TABLE NAME: %s\n"), val);
|
|
||||||
val = PQresultErrorField(res, PG_DIAG_COLUMN_NAME);
|
|
||||||
if (val)
|
|
||||||
appendPQExpBuffer(&workBuf,
|
|
||||||
libpq_gettext("COLUMN NAME: %s\n"), val);
|
|
||||||
val = PQresultErrorField(res, PG_DIAG_DATATYPE_NAME);
|
|
||||||
if (val)
|
|
||||||
appendPQExpBuffer(&workBuf,
|
|
||||||
libpq_gettext("DATATYPE NAME: %s\n"), val);
|
|
||||||
val = PQresultErrorField(res, PG_DIAG_CONSTRAINT_NAME);
|
|
||||||
if (val)
|
|
||||||
appendPQExpBuffer(&workBuf,
|
|
||||||
libpq_gettext("CONSTRAINT NAME: %s\n"), val);
|
|
||||||
}
|
|
||||||
if (conn->verbosity == PQERRORS_VERBOSE)
|
|
||||||
{
|
|
||||||
const char *valf;
|
|
||||||
const char *vall;
|
|
||||||
|
|
||||||
valf = PQresultErrorField(res, PG_DIAG_SOURCE_FILE);
|
|
||||||
vall = PQresultErrorField(res, PG_DIAG_SOURCE_LINE);
|
|
||||||
val = PQresultErrorField(res, PG_DIAG_SOURCE_FUNCTION);
|
|
||||||
if (val || valf || vall)
|
|
||||||
{
|
|
||||||
appendPQExpBufferStr(&workBuf, libpq_gettext("LOCATION: "));
|
|
||||||
if (val)
|
|
||||||
appendPQExpBuffer(&workBuf, libpq_gettext("%s, "), val);
|
|
||||||
if (valf && vall) /* unlikely we'd have just one */
|
|
||||||
appendPQExpBuffer(&workBuf, libpq_gettext("%s:%s"),
|
|
||||||
valf, vall);
|
|
||||||
appendPQExpBufferChar(&workBuf, '\n');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Either save error as current async result, or just emit the notice.
|
* Either save error as current async result, or just emit the notice.
|
||||||
@ -1078,6 +974,157 @@ fail:
|
|||||||
return EOF;
|
return EOF;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Construct an error message from the fields in the given PGresult,
|
||||||
|
* appending it to the contents of "msg".
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
pqBuildErrorMessage3(PQExpBuffer msg, const PGresult *res,
|
||||||
|
PGVerbosity verbosity, PGContextVisibility show_context)
|
||||||
|
{
|
||||||
|
const char *val;
|
||||||
|
const char *querytext = NULL;
|
||||||
|
int querypos = 0;
|
||||||
|
|
||||||
|
/* If we couldn't allocate a PGresult, just say "out of memory" */
|
||||||
|
if (res == NULL)
|
||||||
|
{
|
||||||
|
appendPQExpBuffer(msg, libpq_gettext("out of memory\n"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we don't have any broken-down fields, just return the base message.
|
||||||
|
* This mainly applies if we're given a libpq-generated error result.
|
||||||
|
*/
|
||||||
|
if (res->errFields == NULL)
|
||||||
|
{
|
||||||
|
if (res->errMsg && res->errMsg[0])
|
||||||
|
appendPQExpBufferStr(msg, res->errMsg);
|
||||||
|
else
|
||||||
|
appendPQExpBuffer(msg, libpq_gettext("no error message available\n"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Else build error message from relevant fields */
|
||||||
|
val = PQresultErrorField(res, PG_DIAG_SEVERITY);
|
||||||
|
if (val)
|
||||||
|
appendPQExpBuffer(msg, "%s: ", val);
|
||||||
|
if (verbosity == PQERRORS_VERBOSE)
|
||||||
|
{
|
||||||
|
val = PQresultErrorField(res, PG_DIAG_SQLSTATE);
|
||||||
|
if (val)
|
||||||
|
appendPQExpBuffer(msg, "%s: ", val);
|
||||||
|
}
|
||||||
|
val = PQresultErrorField(res, PG_DIAG_MESSAGE_PRIMARY);
|
||||||
|
if (val)
|
||||||
|
appendPQExpBufferStr(msg, val);
|
||||||
|
val = PQresultErrorField(res, PG_DIAG_STATEMENT_POSITION);
|
||||||
|
if (val)
|
||||||
|
{
|
||||||
|
if (verbosity != PQERRORS_TERSE && res->errQuery != NULL)
|
||||||
|
{
|
||||||
|
/* emit position as a syntax cursor display */
|
||||||
|
querytext = res->errQuery;
|
||||||
|
querypos = atoi(val);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* emit position as text addition to primary message */
|
||||||
|
/* translator: %s represents a digit string */
|
||||||
|
appendPQExpBuffer(msg, libpq_gettext(" at character %s"),
|
||||||
|
val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
val = PQresultErrorField(res, PG_DIAG_INTERNAL_POSITION);
|
||||||
|
if (val)
|
||||||
|
{
|
||||||
|
querytext = PQresultErrorField(res, PG_DIAG_INTERNAL_QUERY);
|
||||||
|
if (verbosity != PQERRORS_TERSE && querytext != NULL)
|
||||||
|
{
|
||||||
|
/* emit position as a syntax cursor display */
|
||||||
|
querypos = atoi(val);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* emit position as text addition to primary message */
|
||||||
|
/* translator: %s represents a digit string */
|
||||||
|
appendPQExpBuffer(msg, libpq_gettext(" at character %s"),
|
||||||
|
val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
appendPQExpBufferChar(msg, '\n');
|
||||||
|
if (verbosity != PQERRORS_TERSE)
|
||||||
|
{
|
||||||
|
if (querytext && querypos > 0)
|
||||||
|
reportErrorPosition(msg, querytext, querypos,
|
||||||
|
res->client_encoding);
|
||||||
|
val = PQresultErrorField(res, PG_DIAG_MESSAGE_DETAIL);
|
||||||
|
if (val)
|
||||||
|
appendPQExpBuffer(msg, libpq_gettext("DETAIL: %s\n"), val);
|
||||||
|
val = PQresultErrorField(res, PG_DIAG_MESSAGE_HINT);
|
||||||
|
if (val)
|
||||||
|
appendPQExpBuffer(msg, libpq_gettext("HINT: %s\n"), val);
|
||||||
|
val = PQresultErrorField(res, PG_DIAG_INTERNAL_QUERY);
|
||||||
|
if (val)
|
||||||
|
appendPQExpBuffer(msg, libpq_gettext("QUERY: %s\n"), val);
|
||||||
|
if (show_context == PQSHOW_CONTEXT_ALWAYS ||
|
||||||
|
(show_context == PQSHOW_CONTEXT_ERRORS &&
|
||||||
|
res->resultStatus == PGRES_FATAL_ERROR))
|
||||||
|
{
|
||||||
|
val = PQresultErrorField(res, PG_DIAG_CONTEXT);
|
||||||
|
if (val)
|
||||||
|
appendPQExpBuffer(msg, libpq_gettext("CONTEXT: %s\n"),
|
||||||
|
val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (verbosity == PQERRORS_VERBOSE)
|
||||||
|
{
|
||||||
|
val = PQresultErrorField(res, PG_DIAG_SCHEMA_NAME);
|
||||||
|
if (val)
|
||||||
|
appendPQExpBuffer(msg,
|
||||||
|
libpq_gettext("SCHEMA NAME: %s\n"), val);
|
||||||
|
val = PQresultErrorField(res, PG_DIAG_TABLE_NAME);
|
||||||
|
if (val)
|
||||||
|
appendPQExpBuffer(msg,
|
||||||
|
libpq_gettext("TABLE NAME: %s\n"), val);
|
||||||
|
val = PQresultErrorField(res, PG_DIAG_COLUMN_NAME);
|
||||||
|
if (val)
|
||||||
|
appendPQExpBuffer(msg,
|
||||||
|
libpq_gettext("COLUMN NAME: %s\n"), val);
|
||||||
|
val = PQresultErrorField(res, PG_DIAG_DATATYPE_NAME);
|
||||||
|
if (val)
|
||||||
|
appendPQExpBuffer(msg,
|
||||||
|
libpq_gettext("DATATYPE NAME: %s\n"), val);
|
||||||
|
val = PQresultErrorField(res, PG_DIAG_CONSTRAINT_NAME);
|
||||||
|
if (val)
|
||||||
|
appendPQExpBuffer(msg,
|
||||||
|
libpq_gettext("CONSTRAINT NAME: %s\n"), val);
|
||||||
|
}
|
||||||
|
if (verbosity == PQERRORS_VERBOSE)
|
||||||
|
{
|
||||||
|
const char *valf;
|
||||||
|
const char *vall;
|
||||||
|
|
||||||
|
valf = PQresultErrorField(res, PG_DIAG_SOURCE_FILE);
|
||||||
|
vall = PQresultErrorField(res, PG_DIAG_SOURCE_LINE);
|
||||||
|
val = PQresultErrorField(res, PG_DIAG_SOURCE_FUNCTION);
|
||||||
|
if (val || valf || vall)
|
||||||
|
{
|
||||||
|
appendPQExpBufferStr(msg, libpq_gettext("LOCATION: "));
|
||||||
|
if (val)
|
||||||
|
appendPQExpBuffer(msg, libpq_gettext("%s, "), val);
|
||||||
|
if (valf && vall) /* unlikely we'd have just one */
|
||||||
|
appendPQExpBuffer(msg, libpq_gettext("%s:%s"),
|
||||||
|
valf, vall);
|
||||||
|
appendPQExpBufferChar(msg, '\n');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Add an error-location display to the error message under construction.
|
* Add an error-location display to the error message under construction.
|
||||||
*
|
*
|
||||||
|
@ -463,6 +463,9 @@ extern PGresult *PQfn(PGconn *conn,
|
|||||||
extern ExecStatusType PQresultStatus(const PGresult *res);
|
extern ExecStatusType PQresultStatus(const PGresult *res);
|
||||||
extern char *PQresStatus(ExecStatusType status);
|
extern char *PQresStatus(ExecStatusType status);
|
||||||
extern char *PQresultErrorMessage(const PGresult *res);
|
extern char *PQresultErrorMessage(const PGresult *res);
|
||||||
|
extern char *PQresultVerboseErrorMessage(const PGresult *res,
|
||||||
|
PGVerbosity verbosity,
|
||||||
|
PGContextVisibility show_context);
|
||||||
extern char *PQresultErrorField(const PGresult *res, int fieldcode);
|
extern char *PQresultErrorField(const PGresult *res, int fieldcode);
|
||||||
extern int PQntuples(const PGresult *res);
|
extern int PQntuples(const PGresult *res);
|
||||||
extern int PQnfields(const PGresult *res);
|
extern int PQnfields(const PGresult *res);
|
||||||
|
@ -197,6 +197,7 @@ struct pg_result
|
|||||||
*/
|
*/
|
||||||
char *errMsg; /* error message, or NULL if no error */
|
char *errMsg; /* error message, or NULL if no error */
|
||||||
PGMessageField *errFields; /* message broken into fields */
|
PGMessageField *errFields; /* message broken into fields */
|
||||||
|
char *errQuery; /* text of triggering query, if available */
|
||||||
|
|
||||||
/* All NULL attributes in the query result point to this null string */
|
/* All NULL attributes in the query result point to this null string */
|
||||||
char null_field[1];
|
char null_field[1];
|
||||||
@ -575,6 +576,8 @@ extern char *pqBuildStartupPacket3(PGconn *conn, int *packetlen,
|
|||||||
const PQEnvironmentOption *options);
|
const PQEnvironmentOption *options);
|
||||||
extern void pqParseInput3(PGconn *conn);
|
extern void pqParseInput3(PGconn *conn);
|
||||||
extern int pqGetErrorNotice3(PGconn *conn, bool isError);
|
extern int pqGetErrorNotice3(PGconn *conn, bool isError);
|
||||||
|
extern void pqBuildErrorMessage3(PQExpBuffer msg, const PGresult *res,
|
||||||
|
PGVerbosity verbosity, PGContextVisibility show_context);
|
||||||
extern int pqGetCopyData3(PGconn *conn, char **buffer, int async);
|
extern int pqGetCopyData3(PGconn *conn, char **buffer, int async);
|
||||||
extern int pqGetline3(PGconn *conn, char *s, int maxlen);
|
extern int pqGetline3(PGconn *conn, char *s, int maxlen);
|
||||||
extern int pqGetlineAsync3(PGconn *conn, char *buffer, int bufsize);
|
extern int pqGetlineAsync3(PGconn *conn, char *buffer, int bufsize);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user