diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index 385cb59927..e8afc247af 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -1717,6 +1717,20 @@ Tue Oct 26 21:40:57 CEST 1999
+
+ \errverbose
+
+
+
+ Repeats the most recent server error message at maximum
+ verbosity, as though VERBOSITY were set
+ to verbose> and SHOW_CONTEXT were
+ set to always>.
+
+
+
+
+
\f [ string ]
@@ -3244,6 +3258,8 @@ bar
that context will be shown in error messages, but not in notice or
warning messages). This setting has no effect
when VERBOSITY> is set to terse>.
+ (See also \errverbose>, for use when you want a verbose
+ version of the error you just got.)
@@ -3286,6 +3302,8 @@ bar
This variable can be set to the values default>,
verbose>, or terse> to control the verbosity
of error reports.
+ (See also \errverbose>, for use when you want a verbose
+ version of the error you just got.)
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 50dc43bf61..3401b5183b 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -822,6 +822,28 @@ exec_command(const char *cmd,
}
}
+ /* \errverbose -- display verbose message from last failed query */
+ else if (strcmp(cmd, "errverbose") == 0)
+ {
+ if (pset.last_error_result)
+ {
+ char *msg;
+
+ msg = PQresultVerboseErrorMessage(pset.last_error_result,
+ PQERRORS_VERBOSE,
+ PQSHOW_CONTEXT_ALWAYS);
+ if (msg)
+ {
+ psql_error("%s", msg);
+ PQfreemem(msg);
+ }
+ else
+ puts(_("out of memory"));
+ }
+ else
+ puts(_("There was no previous error."));
+ }
+
/* \f -- change field separator */
else if (strcmp(cmd, "f") == 0)
{
diff --git a/src/bin/psql/common.c b/src/bin/psql/common.c
index 892058e9ac..a2a07fb538 100644
--- a/src/bin/psql/common.c
+++ b/src/bin/psql/common.c
@@ -497,6 +497,33 @@ AcceptResult(const PGresult *result)
}
+/*
+ * ClearOrSaveResult
+ *
+ * If the result represents an error, remember it for possible display by
+ * \errverbose. Otherwise, just PQclear() it.
+ */
+static void
+ClearOrSaveResult(PGresult *result)
+{
+ if (result)
+ {
+ switch (PQresultStatus(result))
+ {
+ case PGRES_NONFATAL_ERROR:
+ case PGRES_FATAL_ERROR:
+ if (pset.last_error_result)
+ PQclear(pset.last_error_result);
+ pset.last_error_result = result;
+ break;
+
+ default:
+ PQclear(result);
+ break;
+ }
+ }
+}
+
/*
* PSQLexec
@@ -548,7 +575,7 @@ PSQLexec(const char *query)
if (!AcceptResult(res))
{
- PQclear(res);
+ ClearOrSaveResult(res);
res = NULL;
}
@@ -590,7 +617,7 @@ PSQLexecWatch(const char *query, const printQueryOpt *opt)
if (!AcceptResult(res))
{
- PQclear(res);
+ ClearOrSaveResult(res);
return 0;
}
@@ -1077,11 +1104,11 @@ SendQuery(const char *query)
if (PQresultStatus(results) != PGRES_COMMAND_OK)
{
psql_error("%s", PQerrorMessage(pset.db));
- PQclear(results);
+ ClearOrSaveResult(results);
ResetCancelConn();
goto sendquery_cleanup;
}
- PQclear(results);
+ ClearOrSaveResult(results);
transaction_status = PQtransactionStatus(pset.db);
}
@@ -1102,11 +1129,11 @@ SendQuery(const char *query)
if (PQresultStatus(results) != PGRES_COMMAND_OK)
{
psql_error("%s", PQerrorMessage(pset.db));
- PQclear(results);
+ ClearOrSaveResult(results);
ResetCancelConn();
goto sendquery_cleanup;
}
- PQclear(results);
+ ClearOrSaveResult(results);
on_error_rollback_savepoint = true;
}
}
@@ -1202,7 +1229,7 @@ SendQuery(const char *query)
if (PQresultStatus(svptres) != PGRES_COMMAND_OK)
{
psql_error("%s", PQerrorMessage(pset.db));
- PQclear(svptres);
+ ClearOrSaveResult(svptres);
OK = false;
PQclear(results);
@@ -1213,7 +1240,7 @@ SendQuery(const char *query)
}
}
- PQclear(results);
+ ClearOrSaveResult(results);
/* Possible microtiming output */
if (pset.timing)
@@ -1299,7 +1326,7 @@ ExecQueryUsingCursor(const char *query, double *elapsed_msec)
results = PQexec(pset.db, "BEGIN");
OK = AcceptResult(results) &&
(PQresultStatus(results) == PGRES_COMMAND_OK);
- PQclear(results);
+ ClearOrSaveResult(results);
if (!OK)
return false;
started_txn = true;
@@ -1313,7 +1340,7 @@ ExecQueryUsingCursor(const char *query, double *elapsed_msec)
results = PQexec(pset.db, buf.data);
OK = AcceptResult(results) &&
(PQresultStatus(results) == PGRES_COMMAND_OK);
- PQclear(results);
+ ClearOrSaveResult(results);
termPQExpBuffer(&buf);
if (!OK)
goto cleanup;
@@ -1384,7 +1411,7 @@ ExecQueryUsingCursor(const char *query, double *elapsed_msec)
OK = AcceptResult(results);
Assert(!OK);
- PQclear(results);
+ ClearOrSaveResult(results);
break;
}
@@ -1392,7 +1419,7 @@ ExecQueryUsingCursor(const char *query, double *elapsed_msec)
{
/* StoreQueryTuple will complain if not exactly one row */
OK = StoreQueryTuple(results);
- PQclear(results);
+ ClearOrSaveResult(results);
break;
}
@@ -1415,7 +1442,7 @@ ExecQueryUsingCursor(const char *query, double *elapsed_msec)
printQuery(results, &my_popt, fout, is_pager, pset.logfile);
- PQclear(results);
+ ClearOrSaveResult(results);
/* after the first result set, disallow header decoration */
my_popt.topt.start_table = false;
@@ -1473,14 +1500,14 @@ cleanup:
OK = AcceptResult(results) &&
(PQresultStatus(results) == PGRES_COMMAND_OK);
}
- PQclear(results);
+ ClearOrSaveResult(results);
if (started_txn)
{
results = PQexec(pset.db, OK ? "COMMIT" : "ROLLBACK");
OK &= AcceptResult(results) &&
(PQresultStatus(results) == PGRES_COMMAND_OK);
- PQclear(results);
+ ClearOrSaveResult(results);
}
if (pset.timing)
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index 59f6f259dc..c6f0993018 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -168,10 +168,11 @@ slashUsage(unsigned short int pager)
* Use "psql --help=commands | wc" to count correctly. It's okay to count
* the USE_READLINE line even in builds without that.
*/
- output = PageOutput(109, pager ? &(pset.popt.topt) : NULL);
+ output = PageOutput(110, pager ? &(pset.popt.topt) : NULL);
fprintf(output, _("General\n"));
fprintf(output, _(" \\copyright show PostgreSQL usage and distribution terms\n"));
+ fprintf(output, _(" \\errverbose show most recent error message at maximum verbosity\n"));
fprintf(output, _(" \\g [FILE] or ; execute query (and send results to file or |pipe)\n"));
fprintf(output, _(" \\gset [PREFIX] execute query and store results in psql variables\n"));
fprintf(output, _(" \\q quit psql\n"));
diff --git a/src/bin/psql/settings.h b/src/bin/psql/settings.h
index 159a7a5579..ae30b2e60e 100644
--- a/src/bin/psql/settings.h
+++ b/src/bin/psql/settings.h
@@ -86,6 +86,8 @@ typedef struct _psqlSettings
FILE *copyStream; /* Stream to read/write for \copy command */
+ PGresult *last_error_result; /* most recent error result, if any */
+
printQueryOpt popt;
char *gfname; /* one-shot file output argument for \g */
diff --git a/src/bin/psql/startup.c b/src/bin/psql/startup.c
index 07e94d064a..b96cdc445e 100644
--- a/src/bin/psql/startup.c
+++ b/src/bin/psql/startup.c
@@ -135,6 +135,7 @@ main(int argc, char *argv[])
pset.queryFout = stdout;
pset.queryFoutPipe = false;
pset.copyStream = NULL;
+ pset.last_error_result = NULL;
pset.cur_cmd_source = stdin;
pset.cur_cmd_interactive = false;
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index eb592bb395..688d92a452 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -1280,7 +1280,7 @@ psql_completion(const char *text, int start, int end)
"\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl", "\\dL",
"\\dm", "\\dn", "\\do", "\\dO", "\\dp", "\\drds", "\\ds", "\\dS",
"\\dt", "\\dT", "\\dv", "\\du", "\\dx", "\\dy",
- "\\e", "\\echo", "\\ef", "\\encoding", "\\ev",
+ "\\e", "\\echo", "\\ef", "\\encoding", "\\errverbose", "\\ev",
"\\f", "\\g", "\\gset", "\\h", "\\help", "\\H", "\\i", "\\ir", "\\l",
"\\lo_import", "\\lo_export", "\\lo_list", "\\lo_unlink",
"\\o", "\\p", "\\password", "\\prompt", "\\pset", "\\q", "\\qecho", "\\r",