1
0
mirror of https://github.com/postgres/postgres.git synced 2025-04-29 13:56:47 +03:00

Add a "SQLSTATE-only" error verbosity option to libpq and psql.

This is intended for use mostly in test scripts for external tools,
which could do without cross-PG-version variations in error message
wording.  Of course, the SQLSTATE isn't guaranteed stable either, but
it should be more so than the error message text.

Note: there's a bit of an ABI change for libpq here, but it seems
OK because if somebody compiles against a newer version of libpq-fe.h,
and then tries to pass PQERRORS_SQLSTATE to PQsetErrorVerbosity()
of an older libpq library, it will be accepted and then act like
PQERRORS_DEFAULT, thanks to the way the tests in pqBuildErrorMessage3
have historically been phrased.  That seems acceptable.

Didier Gautheron, reviewed by Dagfinn Ilmari Mannsåker

Discussion: https://postgr.es/m/CAJRYxuKyj4zA+JGVrtx8OWAuBfE-_wN4sUMK4H49EuPed=mOBw@mail.gmail.com
This commit is contained in:
Tom Lane 2019-04-04 17:22:02 -04:00
parent 413ccaa74d
commit 7bac3acab4
9 changed files with 98 additions and 25 deletions

View File

@ -6014,21 +6014,30 @@ typedef enum
{ {
PQERRORS_TERSE, PQERRORS_TERSE,
PQERRORS_DEFAULT, PQERRORS_DEFAULT,
PQERRORS_VERBOSE PQERRORS_VERBOSE,
PQERRORS_SQLSTATE
} PGVerbosity; } PGVerbosity;
PGVerbosity PQsetErrorVerbosity(PGconn *conn, PGVerbosity verbosity); PGVerbosity PQsetErrorVerbosity(PGconn *conn, PGVerbosity verbosity);
</synopsis> </synopsis>
<function>PQsetErrorVerbosity</function> sets the verbosity mode, returning <function>PQsetErrorVerbosity</function> sets the verbosity mode,
the connection's previous setting. In <firstterm>TERSE</firstterm> mode, returning the connection's previous setting.
returned messages include severity, primary text, and position only; In <firstterm>TERSE</firstterm> mode, returned messages include
this will normally fit on a single line. The default mode produces severity, primary text, and position only; this will normally fit on a
messages that include the above plus any detail, hint, or context single line. The default mode produces messages that include the above
fields (these might span multiple lines). The <firstterm>VERBOSE</firstterm> plus any detail, hint, or context fields (these might span multiple
mode includes all available fields. Changing the verbosity does not lines). The <firstterm>VERBOSE</firstterm> mode includes all available
affect the messages available from already-existing fields. The <firstterm>SQLSTATE</firstterm> mode includes only the
<structname>PGresult</structname> objects, only subsequently-created ones. error severity and the <symbol>SQLSTATE</symbol> error code, if one is
available (if not, the output is like <firstterm>TERSE</firstterm>
mode).
</para>
<para>
Changing the verbosity setting does not affect the messages available
from already-existing <structname>PGresult</structname> objects, only
subsequently-created ones.
(But see <function>PQresultVerboseErrorMessage</function> if you (But see <function>PQresultVerboseErrorMessage</function> if you
want to print a previous error with a different verbosity.) want to print a previous error with a different verbosity.)
</para> </para>
@ -6061,13 +6070,19 @@ PGContextVisibility PQsetErrorContextVisibility(PGconn *conn, PGContextVisibilit
<function>PQsetErrorContextVisibility</function> sets the context display mode, <function>PQsetErrorContextVisibility</function> sets the context display mode,
returning the connection's previous setting. This mode controls returning the connection's previous setting. This mode controls
whether the <literal>CONTEXT</literal> field is included in messages whether the <literal>CONTEXT</literal> field is included in messages.
(unless the verbosity setting is <firstterm>TERSE</firstterm>, in which The <firstterm>NEVER</firstterm> mode
case <literal>CONTEXT</literal> is never shown). The <firstterm>NEVER</firstterm> mode
never includes <literal>CONTEXT</literal>, while <firstterm>ALWAYS</firstterm> always never includes <literal>CONTEXT</literal>, while <firstterm>ALWAYS</firstterm> always
includes it if available. In <firstterm>ERRORS</firstterm> mode (the includes it if available. In <firstterm>ERRORS</firstterm> mode (the
default), <literal>CONTEXT</literal> fields are included only for error default), <literal>CONTEXT</literal> fields are included only in error
messages, not for notices and warnings. Changing this mode does not messages, not in notices and warnings.
(However, if the verbosity setting is <firstterm>TERSE</firstterm>
or <firstterm>SQLSTATE</firstterm>, <literal>CONTEXT</literal> fields
are omitted regardless of the context display mode.)
</para>
<para>
Changing this mode does not
affect the messages available from affect the messages available from
already-existing <structname>PGresult</structname> objects, only already-existing <structname>PGresult</structname> objects, only
subsequently-created ones. subsequently-created ones.

View File

@ -3892,7 +3892,8 @@ bar
messages from the server. The default is <literal>errors</literal> (meaning messages from the server. The default is <literal>errors</literal> (meaning
that context will be shown in error messages, but not in notice or that context will be shown in error messages, but not in notice or
warning messages). This setting has no effect warning messages). This setting has no effect
when <varname>VERBOSITY</varname> is set to <literal>terse</literal>. when <varname>VERBOSITY</varname> is set to <literal>terse</literal>
or <literal>sqlstate</literal>.
(See also <command>\errverbose</command>, for use when you want a verbose (See also <command>\errverbose</command>, for use when you want a verbose
version of the error you just got.) version of the error you just got.)
</para> </para>
@ -3946,8 +3947,9 @@ bar
<listitem> <listitem>
<para> <para>
This variable can be set to the values <literal>default</literal>, This variable can be set to the values <literal>default</literal>,
<literal>verbose</literal>, or <literal>terse</literal> to control the verbosity <literal>verbose</literal>, <literal>terse</literal>,
of error reports. or <literal>sqlstate</literal> to control the verbosity of error
reports.
(See also <command>\errverbose</command>, for use when you want a verbose (See also <command>\errverbose</command>, for use when you want a verbose
version of the error you just got.) version of the error you just got.)
</para> </para>

View File

@ -340,7 +340,7 @@ helpVariables(unsigned short int pager)
* Windows builds currently print one more line than non-Windows builds. * Windows builds currently print one more line than non-Windows builds.
* Using the larger number is fine. * Using the larger number is fine.
*/ */
output = PageOutput(156, pager ? &(pset.popt.topt) : NULL); output = PageOutput(158, pager ? &(pset.popt.topt) : NULL);
fprintf(output, _("List of specially treated variables\n\n")); fprintf(output, _("List of specially treated variables\n\n"));
@ -414,7 +414,7 @@ helpVariables(unsigned short int pager)
fprintf(output, _(" USER\n" fprintf(output, _(" USER\n"
" the currently connected database user\n")); " the currently connected database user\n"));
fprintf(output, _(" VERBOSITY\n" fprintf(output, _(" VERBOSITY\n"
" controls verbosity of error reports [default, verbose, terse]\n")); " controls verbosity of error reports [default, verbose, terse, sqlstate]\n"));
fprintf(output, _(" VERSION\n" fprintf(output, _(" VERSION\n"
" VERSION_NAME\n" " VERSION_NAME\n"
" VERSION_NUM\n" " VERSION_NUM\n"

View File

@ -1110,13 +1110,15 @@ verbosity_hook(const char *newval)
Assert(newval != NULL); /* else substitute hook messed up */ Assert(newval != NULL); /* else substitute hook messed up */
if (pg_strcasecmp(newval, "default") == 0) if (pg_strcasecmp(newval, "default") == 0)
pset.verbosity = PQERRORS_DEFAULT; pset.verbosity = PQERRORS_DEFAULT;
else if (pg_strcasecmp(newval, "terse") == 0)
pset.verbosity = PQERRORS_TERSE;
else if (pg_strcasecmp(newval, "verbose") == 0) else if (pg_strcasecmp(newval, "verbose") == 0)
pset.verbosity = PQERRORS_VERBOSE; pset.verbosity = PQERRORS_VERBOSE;
else if (pg_strcasecmp(newval, "terse") == 0)
pset.verbosity = PQERRORS_TERSE;
else if (pg_strcasecmp(newval, "sqlstate") == 0)
pset.verbosity = PQERRORS_SQLSTATE;
else else
{ {
PsqlVarEnumError("VERBOSITY", newval, "default, terse, verbose"); PsqlVarEnumError("VERBOSITY", newval, "default, verbose, terse, sqlstate");
return false; return false;
} }

View File

@ -3652,7 +3652,7 @@ psql_completion(const char *text, int start, int end)
else if (TailMatchesCS("SHOW_CONTEXT")) else if (TailMatchesCS("SHOW_CONTEXT"))
COMPLETE_WITH_CS("never", "errors", "always"); COMPLETE_WITH_CS("never", "errors", "always");
else if (TailMatchesCS("VERBOSITY")) else if (TailMatchesCS("VERBOSITY"))
COMPLETE_WITH_CS("default", "verbose", "terse"); COMPLETE_WITH_CS("default", "verbose", "terse", "sqlstate");
} }
else if (TailMatchesCS("\\sf*")) else if (TailMatchesCS("\\sf*"))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_routines, NULL); COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_routines, NULL);

View File

@ -1017,6 +1017,24 @@ pqBuildErrorMessage3(PQExpBuffer msg, const PGresult *res,
val = PQresultErrorField(res, PG_DIAG_SEVERITY); val = PQresultErrorField(res, PG_DIAG_SEVERITY);
if (val) if (val)
appendPQExpBuffer(msg, "%s: ", val); appendPQExpBuffer(msg, "%s: ", val);
if (verbosity == PQERRORS_SQLSTATE)
{
/*
* If we have a SQLSTATE, print that and nothing else. If not (which
* shouldn't happen for server-generated errors, but might possibly
* happen for libpq-generated ones), fall back to TERSE format, as
* that seems better than printing nothing at all.
*/
val = PQresultErrorField(res, PG_DIAG_SQLSTATE);
if (val)
{
appendPQExpBuffer(msg, "%s\n", val);
return;
}
verbosity = PQERRORS_TERSE;
}
if (verbosity == PQERRORS_VERBOSE) if (verbosity == PQERRORS_VERBOSE)
{ {
val = PQresultErrorField(res, PG_DIAG_SQLSTATE); val = PQresultErrorField(res, PG_DIAG_SQLSTATE);

View File

@ -112,7 +112,8 @@ typedef enum
{ {
PQERRORS_TERSE, /* single-line error messages */ PQERRORS_TERSE, /* single-line error messages */
PQERRORS_DEFAULT, /* recommended style */ PQERRORS_DEFAULT, /* recommended style */
PQERRORS_VERBOSE /* all the facts, ma'am */ PQERRORS_VERBOSE, /* all the facts, ma'am */
PQERRORS_SQLSTATE /* only error severity and SQLSTATE code */
} PGVerbosity; } PGVerbosity;
typedef enum typedef enum

View File

@ -4491,6 +4491,26 @@ number of rows: 0
last error message: table "this_table_does_not_exist" does not exist last error message: table "this_table_does_not_exist" does not exist
\echo 'last error code:' :LAST_ERROR_SQLSTATE \echo 'last error code:' :LAST_ERROR_SQLSTATE
last error code: 42P01 last error code: 42P01
-- nondefault verbosity error settings (except verbose, which is too unstable)
\set VERBOSITY terse
SELECT 1 UNION;
ERROR: syntax error at or near ";" at character 15
\echo 'error:' :ERROR
error: true
\echo 'error code:' :SQLSTATE
error code: 42601
\echo 'last error message:' :LAST_ERROR_MESSAGE
last error message: syntax error at or near ";"
\set VERBOSITY sqlstate
SELECT 1/0;
ERROR: 22012
\echo 'error:' :ERROR
error: true
\echo 'error code:' :SQLSTATE
error code: 22012
\echo 'last error message:' :LAST_ERROR_MESSAGE
last error message: division by zero
\set VERBOSITY default
-- working \gdesc -- working \gdesc
SELECT 3 AS three, 4 AS four \gdesc SELECT 3 AS three, 4 AS four \gdesc
Column | Type Column | Type

View File

@ -1001,6 +1001,21 @@ DROP TABLE this_table_does_not_exist;
\echo 'last error message:' :LAST_ERROR_MESSAGE \echo 'last error message:' :LAST_ERROR_MESSAGE
\echo 'last error code:' :LAST_ERROR_SQLSTATE \echo 'last error code:' :LAST_ERROR_SQLSTATE
-- nondefault verbosity error settings (except verbose, which is too unstable)
\set VERBOSITY terse
SELECT 1 UNION;
\echo 'error:' :ERROR
\echo 'error code:' :SQLSTATE
\echo 'last error message:' :LAST_ERROR_MESSAGE
\set VERBOSITY sqlstate
SELECT 1/0;
\echo 'error:' :ERROR
\echo 'error code:' :SQLSTATE
\echo 'last error message:' :LAST_ERROR_MESSAGE
\set VERBOSITY default
-- working \gdesc -- working \gdesc
SELECT 3 AS three, 4 AS four \gdesc SELECT 3 AS three, 4 AS four \gdesc
\echo 'error:' :ERROR \echo 'error:' :ERROR