I wrote: > Attached is a libpq-portion-only version of a patch doing it this way. > I've not yet looked at the psql part of your patch.
Here's an update for the psql side. regards, tom lane
diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml index 385cb59..330127a 100644 *** a/doc/src/sgml/ref/psql-ref.sgml --- b/doc/src/sgml/ref/psql-ref.sgml *************** Tue Oct 26 21:40:57 CEST 1999 *** 1718,1723 **** --- 1718,1737 ---- <varlistentry> + <term><literal>\errverbose</literal></term> + + <listitem> + <para> + Repeats the most recent server error or notice 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> diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c index 50dc43b..3401b51 100644 *** a/src/bin/psql/command.c --- b/src/bin/psql/command.c *************** exec_command(const char *cmd, *** 822,827 **** --- 822,849 ---- } } + /* \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 892058e..a2a07fb 100644 *** a/src/bin/psql/common.c --- b/src/bin/psql/common.c *************** AcceptResult(const PGresult *result) *** 497,502 **** --- 497,529 ---- } + /* + * 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 *************** PSQLexec(const char *query) *** 548,554 **** if (!AcceptResult(res)) { ! PQclear(res); res = NULL; } --- 575,581 ---- if (!AcceptResult(res)) { ! ClearOrSaveResult(res); res = NULL; } *************** PSQLexecWatch(const char *query, const p *** 590,596 **** if (!AcceptResult(res)) { ! PQclear(res); return 0; } --- 617,623 ---- if (!AcceptResult(res)) { ! ClearOrSaveResult(res); return 0; } *************** SendQuery(const char *query) *** 1077,1087 **** if (PQresultStatus(results) != PGRES_COMMAND_OK) { psql_error("%s", PQerrorMessage(pset.db)); ! PQclear(results); ResetCancelConn(); goto sendquery_cleanup; } ! PQclear(results); transaction_status = PQtransactionStatus(pset.db); } --- 1104,1114 ---- if (PQresultStatus(results) != PGRES_COMMAND_OK) { psql_error("%s", PQerrorMessage(pset.db)); ! ClearOrSaveResult(results); ResetCancelConn(); goto sendquery_cleanup; } ! ClearOrSaveResult(results); transaction_status = PQtransactionStatus(pset.db); } *************** SendQuery(const char *query) *** 1102,1112 **** if (PQresultStatus(results) != PGRES_COMMAND_OK) { psql_error("%s", PQerrorMessage(pset.db)); ! PQclear(results); ResetCancelConn(); goto sendquery_cleanup; } ! PQclear(results); on_error_rollback_savepoint = true; } } --- 1129,1139 ---- if (PQresultStatus(results) != PGRES_COMMAND_OK) { psql_error("%s", PQerrorMessage(pset.db)); ! ClearOrSaveResult(results); ResetCancelConn(); goto sendquery_cleanup; } ! ClearOrSaveResult(results); on_error_rollback_savepoint = true; } } *************** SendQuery(const char *query) *** 1202,1208 **** if (PQresultStatus(svptres) != PGRES_COMMAND_OK) { psql_error("%s", PQerrorMessage(pset.db)); ! PQclear(svptres); OK = false; PQclear(results); --- 1229,1235 ---- if (PQresultStatus(svptres) != PGRES_COMMAND_OK) { psql_error("%s", PQerrorMessage(pset.db)); ! ClearOrSaveResult(svptres); OK = false; PQclear(results); *************** SendQuery(const char *query) *** 1213,1219 **** } } ! PQclear(results); /* Possible microtiming output */ if (pset.timing) --- 1240,1246 ---- } } ! ClearOrSaveResult(results); /* Possible microtiming output */ if (pset.timing) *************** ExecQueryUsingCursor(const char *query, *** 1299,1305 **** results = PQexec(pset.db, "BEGIN"); OK = AcceptResult(results) && (PQresultStatus(results) == PGRES_COMMAND_OK); ! PQclear(results); if (!OK) return false; started_txn = true; --- 1326,1332 ---- results = PQexec(pset.db, "BEGIN"); OK = AcceptResult(results) && (PQresultStatus(results) == PGRES_COMMAND_OK); ! ClearOrSaveResult(results); if (!OK) return false; started_txn = true; *************** ExecQueryUsingCursor(const char *query, *** 1313,1319 **** results = PQexec(pset.db, buf.data); OK = AcceptResult(results) && (PQresultStatus(results) == PGRES_COMMAND_OK); ! PQclear(results); termPQExpBuffer(&buf); if (!OK) goto cleanup; --- 1340,1346 ---- results = PQexec(pset.db, buf.data); OK = AcceptResult(results) && (PQresultStatus(results) == PGRES_COMMAND_OK); ! ClearOrSaveResult(results); termPQExpBuffer(&buf); if (!OK) goto cleanup; *************** ExecQueryUsingCursor(const char *query, *** 1384,1390 **** OK = AcceptResult(results); Assert(!OK); ! PQclear(results); break; } --- 1411,1417 ---- OK = AcceptResult(results); Assert(!OK); ! ClearOrSaveResult(results); break; } *************** ExecQueryUsingCursor(const char *query, *** 1392,1398 **** { /* StoreQueryTuple will complain if not exactly one row */ OK = StoreQueryTuple(results); ! PQclear(results); break; } --- 1419,1425 ---- { /* StoreQueryTuple will complain if not exactly one row */ OK = StoreQueryTuple(results); ! ClearOrSaveResult(results); break; } *************** ExecQueryUsingCursor(const char *query, *** 1415,1421 **** printQuery(results, &my_popt, fout, is_pager, pset.logfile); ! PQclear(results); /* after the first result set, disallow header decoration */ my_popt.topt.start_table = false; --- 1442,1448 ---- printQuery(results, &my_popt, fout, is_pager, pset.logfile); ! ClearOrSaveResult(results); /* after the first result set, disallow header decoration */ my_popt.topt.start_table = false; *************** cleanup: *** 1473,1486 **** OK = AcceptResult(results) && (PQresultStatus(results) == PGRES_COMMAND_OK); } ! PQclear(results); if (started_txn) { results = PQexec(pset.db, OK ? "COMMIT" : "ROLLBACK"); OK &= AcceptResult(results) && (PQresultStatus(results) == PGRES_COMMAND_OK); ! PQclear(results); } if (pset.timing) --- 1500,1513 ---- OK = AcceptResult(results) && (PQresultStatus(results) == PGRES_COMMAND_OK); } ! ClearOrSaveResult(results); if (started_txn) { results = PQexec(pset.db, OK ? "COMMIT" : "ROLLBACK"); OK &= AcceptResult(results) && (PQresultStatus(results) == PGRES_COMMAND_OK); ! ClearOrSaveResult(results); } if (pset.timing) diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c index 59f6f25..c6f0993 100644 *** a/src/bin/psql/help.c --- b/src/bin/psql/help.c *************** slashUsage(unsigned short int pager) *** 168,177 **** * 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); fprintf(output, _("General\n")); fprintf(output, _(" \\copyright show PostgreSQL usage and distribution terms\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")); --- 168,178 ---- * Use "psql --help=commands | wc" to count correctly. It's okay to count * the USE_READLINE line even in builds without that. */ ! 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 159a7a5..ae30b2e 100644 *** a/src/bin/psql/settings.h --- b/src/bin/psql/settings.h *************** typedef struct _psqlSettings *** 86,91 **** --- 86,93 ---- 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 07e94d0..b96cdc4 100644 *** a/src/bin/psql/startup.c --- b/src/bin/psql/startup.c *************** main(int argc, char *argv[]) *** 135,140 **** --- 135,141 ---- 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 eb592bb..688d92a 100644 *** a/src/bin/psql/tab-complete.c --- b/src/bin/psql/tab-complete.c *************** psql_completion(const char *text, int st *** 1280,1286 **** "\\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", "\\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", --- 1280,1286 ---- "\\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", "\\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",
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers