Skip to content

Commit 3cc38ca

Browse files
committed
Add psql \errverbose command to see last server error at full 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 a psql feature that redisplays the most recent server error at full verbosity, without needing to make any variable changes or re-execute the failed command. We just need to hang onto the latest error PGresult in case the user executes \errverbose, and then apply libpq's new PQresultVerboseErrorMessage() function to it. This will consume some trivial amount of psql memory, but otherwise the cost when the feature isn't used should be negligible. Alex Shulgin, reviewed by Daniel Vérité, some improvements by me
1 parent e3161b2 commit 3cc38ca

File tree

7 files changed

+88
-17
lines changed

7 files changed

+88
-17
lines changed

doc/src/sgml/ref/psql-ref.sgml

+18
Original file line numberDiff line numberDiff line change
@@ -1717,6 +1717,20 @@ Tue Oct 26 21:40:57 CEST 1999
17171717
</varlistentry>
17181718

17191719

1720+
<varlistentry>
1721+
<term><literal>\errverbose</literal></term>
1722+
1723+
<listitem>
1724+
<para>
1725+
Repeats the most recent server error message at maximum
1726+
verbosity, as though <varname>VERBOSITY</varname> were set
1727+
to <literal>verbose</> and <varname>SHOW_CONTEXT</varname> were
1728+
set to <literal>always</>.
1729+
</para>
1730+
</listitem>
1731+
</varlistentry>
1732+
1733+
17201734
<varlistentry>
17211735
<term><literal>\f [ <replaceable class="parameter">string</replaceable> ]</literal></term>
17221736

@@ -3244,6 +3258,8 @@ bar
32443258
that context will be shown in error messages, but not in notice or
32453259
warning messages). This setting has no effect
32463260
when <varname>VERBOSITY</> is set to <literal>terse</>.
3261+
(See also <command>\errverbose</>, for use when you want a verbose
3262+
version of the error you just got.)
32473263
</para>
32483264
</listitem>
32493265
</varlistentry>
@@ -3286,6 +3302,8 @@ bar
32863302
This variable can be set to the values <literal>default</>,
32873303
<literal>verbose</>, or <literal>terse</> to control the verbosity
32883304
of error reports.
3305+
(See also <command>\errverbose</>, for use when you want a verbose
3306+
version of the error you just got.)
32893307
</para>
32903308
</listitem>
32913309
</varlistentry>

src/bin/psql/command.c

+22
Original file line numberDiff line numberDiff line change
@@ -822,6 +822,28 @@ exec_command(const char *cmd,
822822
}
823823
}
824824

825+
/* \errverbose -- display verbose message from last failed query */
826+
else if (strcmp(cmd, "errverbose") == 0)
827+
{
828+
if (pset.last_error_result)
829+
{
830+
char *msg;
831+
832+
msg = PQresultVerboseErrorMessage(pset.last_error_result,
833+
PQERRORS_VERBOSE,
834+
PQSHOW_CONTEXT_ALWAYS);
835+
if (msg)
836+
{
837+
psql_error("%s", msg);
838+
PQfreemem(msg);
839+
}
840+
else
841+
puts(_("out of memory"));
842+
}
843+
else
844+
puts(_("There was no previous error."));
845+
}
846+
825847
/* \f -- change field separator */
826848
else if (strcmp(cmd, "f") == 0)
827849
{

src/bin/psql/common.c

+42-15
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,33 @@ AcceptResult(const PGresult *result)
497497
}
498498

499499

500+
/*
501+
* ClearOrSaveResult
502+
*
503+
* If the result represents an error, remember it for possible display by
504+
* \errverbose. Otherwise, just PQclear() it.
505+
*/
506+
static void
507+
ClearOrSaveResult(PGresult *result)
508+
{
509+
if (result)
510+
{
511+
switch (PQresultStatus(result))
512+
{
513+
case PGRES_NONFATAL_ERROR:
514+
case PGRES_FATAL_ERROR:
515+
if (pset.last_error_result)
516+
PQclear(pset.last_error_result);
517+
pset.last_error_result = result;
518+
break;
519+
520+
default:
521+
PQclear(result);
522+
break;
523+
}
524+
}
525+
}
526+
500527

501528
/*
502529
* PSQLexec
@@ -548,7 +575,7 @@ PSQLexec(const char *query)
548575

549576
if (!AcceptResult(res))
550577
{
551-
PQclear(res);
578+
ClearOrSaveResult(res);
552579
res = NULL;
553580
}
554581

@@ -590,7 +617,7 @@ PSQLexecWatch(const char *query, const printQueryOpt *opt)
590617

591618
if (!AcceptResult(res))
592619
{
593-
PQclear(res);
620+
ClearOrSaveResult(res);
594621
return 0;
595622
}
596623

@@ -1077,11 +1104,11 @@ SendQuery(const char *query)
10771104
if (PQresultStatus(results) != PGRES_COMMAND_OK)
10781105
{
10791106
psql_error("%s", PQerrorMessage(pset.db));
1080-
PQclear(results);
1107+
ClearOrSaveResult(results);
10811108
ResetCancelConn();
10821109
goto sendquery_cleanup;
10831110
}
1084-
PQclear(results);
1111+
ClearOrSaveResult(results);
10851112
transaction_status = PQtransactionStatus(pset.db);
10861113
}
10871114

@@ -1102,11 +1129,11 @@ SendQuery(const char *query)
11021129
if (PQresultStatus(results) != PGRES_COMMAND_OK)
11031130
{
11041131
psql_error("%s", PQerrorMessage(pset.db));
1105-
PQclear(results);
1132+
ClearOrSaveResult(results);
11061133
ResetCancelConn();
11071134
goto sendquery_cleanup;
11081135
}
1109-
PQclear(results);
1136+
ClearOrSaveResult(results);
11101137
on_error_rollback_savepoint = true;
11111138
}
11121139
}
@@ -1202,7 +1229,7 @@ SendQuery(const char *query)
12021229
if (PQresultStatus(svptres) != PGRES_COMMAND_OK)
12031230
{
12041231
psql_error("%s", PQerrorMessage(pset.db));
1205-
PQclear(svptres);
1232+
ClearOrSaveResult(svptres);
12061233
OK = false;
12071234

12081235
PQclear(results);
@@ -1213,7 +1240,7 @@ SendQuery(const char *query)
12131240
}
12141241
}
12151242

1216-
PQclear(results);
1243+
ClearOrSaveResult(results);
12171244

12181245
/* Possible microtiming output */
12191246
if (pset.timing)
@@ -1299,7 +1326,7 @@ ExecQueryUsingCursor(const char *query, double *elapsed_msec)
12991326
results = PQexec(pset.db, "BEGIN");
13001327
OK = AcceptResult(results) &&
13011328
(PQresultStatus(results) == PGRES_COMMAND_OK);
1302-
PQclear(results);
1329+
ClearOrSaveResult(results);
13031330
if (!OK)
13041331
return false;
13051332
started_txn = true;
@@ -1313,7 +1340,7 @@ ExecQueryUsingCursor(const char *query, double *elapsed_msec)
13131340
results = PQexec(pset.db, buf.data);
13141341
OK = AcceptResult(results) &&
13151342
(PQresultStatus(results) == PGRES_COMMAND_OK);
1316-
PQclear(results);
1343+
ClearOrSaveResult(results);
13171344
termPQExpBuffer(&buf);
13181345
if (!OK)
13191346
goto cleanup;
@@ -1384,15 +1411,15 @@ ExecQueryUsingCursor(const char *query, double *elapsed_msec)
13841411

13851412
OK = AcceptResult(results);
13861413
Assert(!OK);
1387-
PQclear(results);
1414+
ClearOrSaveResult(results);
13881415
break;
13891416
}
13901417

13911418
if (pset.gset_prefix)
13921419
{
13931420
/* StoreQueryTuple will complain if not exactly one row */
13941421
OK = StoreQueryTuple(results);
1395-
PQclear(results);
1422+
ClearOrSaveResult(results);
13961423
break;
13971424
}
13981425

@@ -1415,7 +1442,7 @@ ExecQueryUsingCursor(const char *query, double *elapsed_msec)
14151442

14161443
printQuery(results, &my_popt, fout, is_pager, pset.logfile);
14171444

1418-
PQclear(results);
1445+
ClearOrSaveResult(results);
14191446

14201447
/* after the first result set, disallow header decoration */
14211448
my_popt.topt.start_table = false;
@@ -1473,14 +1500,14 @@ ExecQueryUsingCursor(const char *query, double *elapsed_msec)
14731500
OK = AcceptResult(results) &&
14741501
(PQresultStatus(results) == PGRES_COMMAND_OK);
14751502
}
1476-
PQclear(results);
1503+
ClearOrSaveResult(results);
14771504

14781505
if (started_txn)
14791506
{
14801507
results = PQexec(pset.db, OK ? "COMMIT" : "ROLLBACK");
14811508
OK &= AcceptResult(results) &&
14821509
(PQresultStatus(results) == PGRES_COMMAND_OK);
1483-
PQclear(results);
1510+
ClearOrSaveResult(results);
14841511
}
14851512

14861513
if (pset.timing)

src/bin/psql/help.c

+2-1
Original file line numberDiff line numberDiff line change
@@ -168,10 +168,11 @@ slashUsage(unsigned short int pager)
168168
* Use "psql --help=commands | wc" to count correctly. It's okay to count
169169
* the USE_READLINE line even in builds without that.
170170
*/
171-
output = PageOutput(109, pager ? &(pset.popt.topt) : NULL);
171+
output = PageOutput(110, pager ? &(pset.popt.topt) : NULL);
172172

173173
fprintf(output, _("General\n"));
174174
fprintf(output, _(" \\copyright show PostgreSQL usage and distribution terms\n"));
175+
fprintf(output, _(" \\errverbose show most recent error message at maximum verbosity\n"));
175176
fprintf(output, _(" \\g [FILE] or ; execute query (and send results to file or |pipe)\n"));
176177
fprintf(output, _(" \\gset [PREFIX] execute query and store results in psql variables\n"));
177178
fprintf(output, _(" \\q quit psql\n"));

src/bin/psql/settings.h

+2
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@ typedef struct _psqlSettings
8686

8787
FILE *copyStream; /* Stream to read/write for \copy command */
8888

89+
PGresult *last_error_result; /* most recent error result, if any */
90+
8991
printQueryOpt popt;
9092

9193
char *gfname; /* one-shot file output argument for \g */

src/bin/psql/startup.c

+1
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ main(int argc, char *argv[])
135135
pset.queryFout = stdout;
136136
pset.queryFoutPipe = false;
137137
pset.copyStream = NULL;
138+
pset.last_error_result = NULL;
138139
pset.cur_cmd_source = stdin;
139140
pset.cur_cmd_interactive = false;
140141

src/bin/psql/tab-complete.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -1280,7 +1280,7 @@ psql_completion(const char *text, int start, int end)
12801280
"\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl", "\\dL",
12811281
"\\dm", "\\dn", "\\do", "\\dO", "\\dp", "\\drds", "\\ds", "\\dS",
12821282
"\\dt", "\\dT", "\\dv", "\\du", "\\dx", "\\dy",
1283-
"\\e", "\\echo", "\\ef", "\\encoding", "\\ev",
1283+
"\\e", "\\echo", "\\ef", "\\encoding", "\\errverbose", "\\ev",
12841284
"\\f", "\\g", "\\gset", "\\h", "\\help", "\\H", "\\i", "\\ir", "\\l",
12851285
"\\lo_import", "\\lo_export", "\\lo_list", "\\lo_unlink",
12861286
"\\o", "\\p", "\\password", "\\prompt", "\\pset", "\\q", "\\qecho", "\\r",

0 commit comments

Comments
 (0)