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",