]> granicus.if.org Git - postgresql/commitdiff
Add psql \errverbose command to see last server error at full verbosity.
authorTom Lane <tgl@sss.pgh.pa.us>
Sun, 3 Apr 2016 16:29:55 +0000 (12:29 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Sun, 3 Apr 2016 16:29:55 +0000 (12:29 -0400)
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

doc/src/sgml/ref/psql-ref.sgml
src/bin/psql/command.c
src/bin/psql/common.c
src/bin/psql/help.c
src/bin/psql/settings.h
src/bin/psql/startup.c
src/bin/psql/tab-complete.c

index 385cb599278a4885ad93848ff1d33b3462fa288f..e8afc247afe04eaed53522ea2326e33733ffd6ba 100644 (file)
@@ -1717,6 +1717,20 @@ Tue Oct 26 21:40:57 CEST 1999
       </varlistentry>
 
 
+      <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>
 
@@ -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>
index 50dc43bf61a45a7265368d330149d981443441cf..3401b5183b056bc2b8439e729bb2b0c16a91a060 100644 (file)
@@ -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)
        {
index 892058e9ac833645e583734ab6567a8ffb0bf055..a2a07fb538e8f0acfe606a6c78400e16a6ced0f5 100644 (file)
@@ -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)
index 59f6f259dcda74f9090c6c0af46b916d6cfa506f..c6f0993018fa3e9c41a9eb15a788554b5f4dbb27 100644 (file)
@@ -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"));
index 159a7a5579a942cdb7bebd20826dc3222a9a0e3b..ae30b2e60e3ba333f6994a9296eaf4b7fa1075ba 100644 (file)
@@ -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 */
index 07e94d064a39a8e5c2608cd07f739b8f532efd0d..b96cdc445e4c8b5d4ffac702f7a33ff4a2f144c7 100644 (file)
@@ -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;
 
index eb592bb395fcbc3504b99a7d902e4d09bfd1d9b8..688d92a4520772c471a64b0596affb3fa6d33787 100644 (file)
@@ -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",