aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2016-04-03 12:29:55 -0400
committerTom Lane <tgl@sss.pgh.pa.us>2016-04-03 12:29:55 -0400
commit3cc38ca7d21255721d600eb75d7cc6708c14764b (patch)
tree0e06239939dd831361a1c248c36db94d5ff1e33d
parente3161b231cfaadd4b6438eff2fc1f6cd086f41a9 (diff)
downloadpostgresql-3cc38ca7d21255721d600eb75d7cc6708c14764b.tar.gz
postgresql-3cc38ca7d21255721d600eb75d7cc6708c14764b.zip
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
-rw-r--r--doc/src/sgml/ref/psql-ref.sgml18
-rw-r--r--src/bin/psql/command.c22
-rw-r--r--src/bin/psql/common.c57
-rw-r--r--src/bin/psql/help.c3
-rw-r--r--src/bin/psql/settings.h2
-rw-r--r--src/bin/psql/startup.c1
-rw-r--r--src/bin/psql/tab-complete.c2
7 files changed, 88 insertions, 17 deletions
diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index 385cb599278..e8afc247afe 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -1718,6 +1718,20 @@ Tue Oct 26 21:40:57 CEST 1999
<varlistentry>
+ <term><literal>\errverbose</literal></term>
+
+ <listitem>
+ <para>
+ Repeats the most recent server error message at maximum
+ verbosity, as though <varname>VERBOSITY</varname> were set
+ to <literal>verbose</> and <varname>SHOW_CONTEXT</varname> were
+ set to <literal>always</>.
+ </para>
+ </listitem>
+ </varlistentry>
+
+
+ <varlistentry>
<term><literal>\f [ <replaceable class="parameter">string</replaceable> ]</literal></term>
<listitem>
@@ -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 <varname>VERBOSITY</> is set to <literal>terse</>.
+ (See also <command>\errverbose</>, for use when you want a verbose
+ version of the error you just got.)
</para>
</listitem>
</varlistentry>
@@ -3286,6 +3302,8 @@ bar
This variable can be set to the values <literal>default</>,
<literal>verbose</>, or <literal>terse</> to control the verbosity
of error reports.
+ (See also <command>\errverbose</>, for use when you want a verbose
+ version of the error you just got.)
</para>
</listitem>
</varlistentry>
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 50dc43bf61a..3401b5183b0 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 892058e9ac8..a2a07fb538e 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 59f6f259dcd..c6f0993018f 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 159a7a5579a..ae30b2e60e3 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 07e94d064a3..b96cdc445e4 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 eb592bb395f..688d92a4520 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",