Commit 7bac3aca authored by Tom Lane's avatar Tom Lane

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
parent 413ccaa7
...@@ -6014,21 +6014,30 @@ typedef enum ...@@ -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 ...@@ -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.
......
...@@ -3892,7 +3892,8 @@ bar ...@@ -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 ...@@ -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>
......
...@@ -340,7 +340,7 @@ helpVariables(unsigned short int pager) ...@@ -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) ...@@ -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"
......
...@@ -1110,13 +1110,15 @@ verbosity_hook(const char *newval) ...@@ -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;
} }
......
...@@ -3652,7 +3652,7 @@ psql_completion(const char *text, int start, int end) ...@@ -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);
......
...@@ -1017,6 +1017,24 @@ pqBuildErrorMessage3(PQExpBuffer msg, const PGresult *res, ...@@ -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);
......
...@@ -112,7 +112,8 @@ typedef enum ...@@ -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
......
...@@ -4491,6 +4491,26 @@ number of rows: 0 ...@@ -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
......
...@@ -1001,6 +1001,21 @@ DROP TABLE this_table_does_not_exist; ...@@ -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
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment