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

Reply via email to