On Mon, Mar 14, 2016 at 7:55 PM, Tom Lane <t...@sss.pgh.pa.us> wrote:

> "Shulgin, Oleksandr" <oleksandr.shul...@zalando.de> writes:
> > What I dislike about this POC is all the disruption in libpq, to be
> > honest.
>
> Yeah, I don't much like that either.  But I don't think we can avoid
> some refactoring there; as designed, conversion of an error message into
> user-visible form is too tightly tied to receipt of the message.
>

True.  Attached is a v2 which addresses all of the points raised earlier I
believe.

We still extract the message building part of code from
pqGetErrorNotice3(), but the user-facing change is much more sane now: just
added a new function PQresultVerboseErrorMessage().

> It would be much neater if we could form the verbose message every
> > time and let the client decide where to cut it.  Maybe a bit "too clever"
> > would be to put a \0 char between short message and it's verbose
> > continuation.  The client could then reach the verbose part like this
> > (assuming that libpq did put a verbose part there): msg + strlen(msg) +
> 1.
>
> Blech :-(
>

:-)  That would not work either way, I've just noticed that SQLLEVEL is put
at the start of the message in verbose mode, but not by default.

Thinking about it, though, it seems to me that we could get away with
> always performing both styles of conversion and sticking both strings
> into the PGresult.  That would eat a little more space but not much.
> Then we just need to add API to let the application get at both forms.
>

This is what the v2 basically implements, now complete with help,
tab-complete and documentation changes.  I don't think we can add a usual
simple regression test here reliably, due to LOCATION field which might be
subject to unexpected line number changes.  But do we really need one?

--
Regards,
Alex
From d8d6a65da57d8e14eac4f614d31b19d52f8176d9 Mon Sep 17 00:00:00 2001
From: Oleksandr Shulgin <oleksandr.shul...@zalando.de>
Date: Wed, 6 Jan 2016 14:58:17 +0100
Subject: [PATCH] Add \errverbose psql command and support in libpq

For every error we build both the default and verbose error message,
then store both in PGresult.  The client can then retrieve the verbose
message using the new exported function PGresultVerboseErrorMessage().

In psql we store the verbose error message in pset (if any) for display
when requested by the user with \errverbose.
---
 doc/src/sgml/libpq.sgml             |  40 ++++++++-
 src/bin/psql/command.c              |   9 ++
 src/bin/psql/common.c               |   5 ++
 src/bin/psql/help.c                 |   1 +
 src/bin/psql/settings.h             |   2 +
 src/bin/psql/startup.c              |   1 +
 src/bin/psql/tab-complete.c         |   2 +-
 src/interfaces/libpq/exports.txt    |   1 +
 src/interfaces/libpq/fe-exec.c      |  29 ++++++-
 src/interfaces/libpq/fe-protocol3.c | 166 +++++++++++++++++++++---------------
 src/interfaces/libpq/libpq-fe.h     |   1 +
 src/interfaces/libpq/libpq-int.h    |   3 +-
 12 files changed, 181 insertions(+), 79 deletions(-)

diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index 2328d8f..8b9544f 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -2691,6 +2691,38 @@ char *PQresultErrorMessage(const PGresult *res);
       </listitem>
      </varlistentry>
 
+     <varlistentry id="libpq-pqresultverboseerrormessage">
+      <term>
+       <function>PQresultVerboseErrorMessage</function>
+       <indexterm>
+        <primary>PQresultVerboseErrorMessage</primary>
+       </indexterm>
+      </term>
+
+      <listitem>
+       <para>
+        Returns the most verbose error message associated with the
+        command, or an empty string if there was no error.
+<synopsis>
+char *PQresultVerboseErrorMessage(const PGresult *res);
+</synopsis>
+        If there was an error, the returned string will include a trailing
+        newline.  The caller should not free the result directly. It will
+        be freed when the associated <structname>PGresult</> handle is
+        passed to <function>PQclear</function>.
+       </para>
+
+       <para>
+         If error verbosity is already set to the maximum available level,
+         this will return exactly the same error message
+         as <function>PQresultErrorMessage</function>. Otherwise it can be
+         useful to retrieve a more detailed error message without running
+         the failed query again (while increasing the error verbosity
+         level).
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="libpq-pqresulterrorfield">
       <term><function>PQresultErrorField</function><indexterm><primary>PQresultErrorField</></></term>
       <listitem>
@@ -5579,9 +5611,11 @@ PGVerbosity PQsetErrorVerbosity(PGconn *conn, PGVerbosity verbosity);
       this will normally fit on a single line.  The default mode produces
       messages that include the above plus any detail, hint, or context
       fields (these might span multiple lines).  The <firstterm>VERBOSE</>
-      mode includes all available fields.  Changing the verbosity does not
-      affect the messages available from already-existing
-      <structname>PGresult</> objects, only subsequently-created ones.
+      mode includes all available fields.  It is still possible to
+      retrieve the most verbose error message from an
+      existing <structname>PGresult</> object (without changing the
+      current verbosity level)
+      using <function>PQresultVerboseErrorMessage</>.
      </para>
     </listitem>
    </varlistentry>
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 9750a5b..8ec2bbd 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -822,6 +822,13 @@ exec_command(const char *cmd,
 		}
 	}
 
+	/* \errverbose -- display verbose error message from last result */
+	else if (strcmp(cmd, "errverbose") == 0)
+	{
+		if (pset.verbose_error && *pset.verbose_error)
+			psql_error("%s", pset.verbose_error);
+	}
+
 	/* \f -- change field separator */
 	else if (strcmp(cmd, "f") == 0)
 	{
@@ -1865,6 +1872,8 @@ do_connect(char *dbname, char *user, char *host, char *port)
 			{
 				PQfinish(o_conn);
 				pset.db = NULL;
+				free(pset.verbose_error);
+				pset.verbose_error = NULL;
 			}
 		}
 
diff --git a/src/bin/psql/common.c b/src/bin/psql/common.c
index 2cb2e9b..331addb 100644
--- a/src/bin/psql/common.c
+++ b/src/bin/psql/common.c
@@ -315,6 +315,8 @@ CheckConnection(void)
 			psql_error("Failed.\n");
 			PQfinish(pset.db);
 			pset.db = NULL;
+			free(pset.verbose_error);
+			pset.verbose_error = NULL;
 			ResetCancelConn();
 			UnsyncVariables();
 		}
@@ -1154,6 +1156,9 @@ SendQuery(const char *query)
 		}
 	}
 
+	/* Save the verbose error message (if any) and clear the results. */
+	free(pset.verbose_error);
+	pset.verbose_error = pg_strdup(PQresultVerboseErrorMessage(results));
 	PQclear(results);
 
 	/* Possible microtiming output */
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index 59f6f25..abfa40d 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -172,6 +172,7 @@ slashUsage(unsigned short int pager)
 
 	fprintf(output, _("General\n"));
 	fprintf(output, _("  \\copyright             show PostgreSQL usage and distribution terms\n"));
+	fprintf(output, _("  \\errverbose            show verbose error message from the last query\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 20a6470..bcb16ce 100644
--- a/src/bin/psql/settings.h
+++ b/src/bin/psql/settings.h
@@ -130,6 +130,8 @@ typedef struct _psqlSettings
 	const char *prompt3;
 	PGVerbosity verbosity;		/* current error verbosity level */
 	PGContextVisibility show_context;	/* current context display level */
+	char	   *verbose_error;	/* set if last query resulted in an error
+								   and a verbose message was available */
 } PsqlSettings;
 
 extern PsqlSettings pset;
diff --git a/src/bin/psql/startup.c b/src/bin/psql/startup.c
index 6916f6f..695a84a 100644
--- a/src/bin/psql/startup.c
+++ b/src/bin/psql/startup.c
@@ -130,6 +130,7 @@ main(int argc, char *argv[])
 	pset.progname = get_progname(argv[0]);
 
 	pset.db = NULL;
+	pset.verbose_error = NULL;
 	setDecimalLocale();
 	pset.encoding = PQenv2encoding();
 	pset.queryFout = stdout;
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 6a81416..061da8d 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -1279,7 +1279,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",
diff --git a/src/interfaces/libpq/exports.txt b/src/interfaces/libpq/exports.txt
index c69a4d5..21dd772 100644
--- a/src/interfaces/libpq/exports.txt
+++ b/src/interfaces/libpq/exports.txt
@@ -170,3 +170,4 @@ PQsslStruct               167
 PQsslAttributeNames       168
 PQsslAttribute            169
 PQsetErrorContextVisibility 170
+PQresultVerboseErrorMessage 171
diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c
index 41937c0..72f31a0 100644
--- a/src/interfaces/libpq/fe-exec.c
+++ b/src/interfaces/libpq/fe-exec.c
@@ -158,6 +158,7 @@ PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status)
 	result->events = NULL;
 	result->nEvents = 0;
 	result->errMsg = NULL;
+	result->errMsgVerbose = NULL;
 	result->errFields = NULL;
 	result->null_field[0] = '\0';
 	result->curBlock = NULL;
@@ -627,17 +628,29 @@ pqSetResultError(PGresult *res, const char *msg)
  *		concatenate a new error message to the one already in a PGresult
  */
 void
-pqCatenateResultError(PGresult *res, const char *msg)
+pqCatenateResultError(PGresult *res, const char *msg, const char *msgVerbose)
 {
 	PQExpBufferData errorBuf;
 
 	if (!res || !msg)
 		return;
+
 	initPQExpBuffer(&errorBuf);
 	if (res->errMsg)
 		appendPQExpBufferStr(&errorBuf, res->errMsg);
 	appendPQExpBufferStr(&errorBuf, msg);
 	pqSetResultError(res, errorBuf.data);
+
+	if (msgVerbose)
+	{
+		resetPQExpBuffer(&errorBuf);
+		if (res->errMsgVerbose)
+			appendPQExpBufferStr(&errorBuf, res->errMsgVerbose);
+		appendPQExpBufferStr(&errorBuf, msgVerbose);
+
+		res->errMsgVerbose = pqResultStrdup(res, errorBuf.data);
+	}
+
 	termPQExpBuffer(&errorBuf);
 }
 
@@ -740,7 +753,9 @@ pqSaveErrorResult(PGconn *conn)
 	else
 	{
 		/* Else, concatenate error message to existing async result. */
-		pqCatenateResultError(conn->result, conn->errorMessage.data);
+
+		/* XXX do we need to store verbose error message in conn? */
+		pqCatenateResultError(conn->result, conn->errorMessage.data, NULL);
 	}
 }
 
@@ -2004,7 +2019,7 @@ PQexecFinish(PGconn *conn)
 			if (lastResult->resultStatus == PGRES_FATAL_ERROR &&
 				result->resultStatus == PGRES_FATAL_ERROR)
 			{
-				pqCatenateResultError(lastResult, result->errMsg);
+				pqCatenateResultError(lastResult, result->errMsg, result->errMsgVerbose);
 				PQclear(result);
 				result = lastResult;
 
@@ -2599,6 +2614,14 @@ PQresultErrorMessage(const PGresult *res)
 }
 
 char *
+PQresultVerboseErrorMessage(const PGresult *res)
+{
+	if (!res || !res->errMsgVerbose)
+		return "";
+	return res->errMsgVerbose;
+}
+
+char *
 PQresultErrorField(const PGresult *res, int fieldcode)
 {
 	PGMessageField *pfield;
diff --git a/src/interfaces/libpq/fe-protocol3.c b/src/interfaces/libpq/fe-protocol3.c
index 43898a4..a3365ac 100644
--- a/src/interfaces/libpq/fe-protocol3.c
+++ b/src/interfaces/libpq/fe-protocol3.c
@@ -51,6 +51,8 @@ static int	getParameterStatus(PGconn *conn);
 static int	getNotify(PGconn *conn);
 static int	getCopyStart(PGconn *conn, ExecStatusType copytype);
 static int	getReadyForQuery(PGconn *conn);
+static void buildErrorMessage(const PGconn *conn, const PGresult *res, bool isError,
+					PGVerbosity verbosity, PQExpBuffer workBuf);
 static void reportErrorPosition(PQExpBuffer msg, const char *query,
 					int loc, int encoding);
 static int build_startup_packet(const PGconn *conn, char *packet,
@@ -865,9 +867,7 @@ pqGetErrorNotice3(PGconn *conn, bool isError)
 	PGresult   *res = NULL;
 	PQExpBufferData workBuf;
 	char		id;
-	const char *val;
-	const char *querytext = NULL;
-	int			querypos = 0;
+	const char *sqlstate;
 
 	/*
 	 * Since the fields might be pretty long, we create a temporary
@@ -904,30 +904,90 @@ pqGetErrorNotice3(PGconn *conn, bool isError)
 		pqSaveMessageField(res, id, workBuf.data);
 	}
 
+	/* Save the last SQLSTATE in connection. */
+	sqlstate = PQresultErrorField(res, PG_DIAG_SQLSTATE);
+	if (sqlstate && strlen(sqlstate) < sizeof(conn->last_sqlstate))
+		strcpy(conn->last_sqlstate, sqlstate);
+
+	/* Now build the "overall" error message for PQresultErrorMessage. */
+	resetPQExpBuffer(&workBuf);
+	buildErrorMessage(conn, res, isError, conn->verbosity, &workBuf);
+
 	/*
-	 * Now build the "overall" error message for PQresultErrorMessage.
-	 *
-	 * Also, save the SQLSTATE in conn->last_sqlstate.
+	 * Either save error as current async result, or just emit the notice.
 	 */
-	resetPQExpBuffer(&workBuf);
+	if (isError)
+	{
+		if (res)
+			res->errMsg = pqResultStrdup(res, workBuf.data);
+		pqClearAsyncResult(conn);
+		conn->result = res;
+		if (PQExpBufferDataBroken(workBuf))
+			printfPQExpBuffer(&conn->errorMessage,
+							  libpq_gettext("out of memory"));
+		else
+		{
+			appendPQExpBufferStr(&conn->errorMessage, workBuf.data);
+
+			if (res)
+			{
+				/* Also build the verbose error message. */
+				resetPQExpBuffer(&workBuf);
+				buildErrorMessage(conn, res, isError, PQERRORS_VERBOSE, &workBuf);
+				if (PQExpBufferDataBroken(workBuf))
+					res->errMsgVerbose = pqResultStrdup(res,
+														libpq_gettext("out of memory"));
+				else
+					res->errMsgVerbose = pqResultStrdup(res, workBuf.data);
+			}
+		}
+	}
+	else
+	{
+		/* if we couldn't allocate the result set, just discard the NOTICE */
+		if (res)
+		{
+			/* We can cheat a little here and not copy the message. */
+			res->errMsg = workBuf.data;
+			if (res->noticeHooks.noticeRec != NULL)
+				(*res->noticeHooks.noticeRec) (res->noticeHooks.noticeRecArg, res);
+			PQclear(res);
+		}
+	}
+
+	termPQExpBuffer(&workBuf);
+	return 0;
+
+fail:
+	PQclear(res);
+	termPQExpBuffer(&workBuf);
+	return EOF;
+}
+
+static void
+buildErrorMessage(const PGconn *conn, const PGresult *res, bool isError,
+				  PGVerbosity verbosity, PQExpBuffer workBuf)
+{
+	const char *val;
+	const char *querytext = NULL;
+	int			querypos = 0;
+
 	val = PQresultErrorField(res, PG_DIAG_SEVERITY);
 	if (val)
-		appendPQExpBuffer(&workBuf, "%s:  ", val);
+		appendPQExpBuffer(workBuf, "%s:  ", val);
 	val = PQresultErrorField(res, PG_DIAG_SQLSTATE);
 	if (val)
 	{
-		if (strlen(val) < sizeof(conn->last_sqlstate))
-			strcpy(conn->last_sqlstate, val);
-		if (conn->verbosity == PQERRORS_VERBOSE)
-			appendPQExpBuffer(&workBuf, "%s: ", val);
+		if (verbosity == PQERRORS_VERBOSE)
+			appendPQExpBuffer(workBuf, "%s: ", val);
 	}
 	val = PQresultErrorField(res, PG_DIAG_MESSAGE_PRIMARY);
 	if (val)
-		appendPQExpBufferStr(&workBuf, val);
+		appendPQExpBufferStr(workBuf, val);
 	val = PQresultErrorField(res, PG_DIAG_STATEMENT_POSITION);
 	if (val)
 	{
-		if (conn->verbosity != PQERRORS_TERSE && conn->last_query != NULL)
+		if (verbosity != PQERRORS_TERSE && conn->last_query != NULL)
 		{
 			/* emit position as a syntax cursor display */
 			querytext = conn->last_query;
@@ -937,7 +997,7 @@ pqGetErrorNotice3(PGconn *conn, bool isError)
 		{
 			/* emit position as text addition to primary message */
 			/* translator: %s represents a digit string */
-			appendPQExpBuffer(&workBuf, libpq_gettext(" at character %s"),
+			appendPQExpBuffer(workBuf, libpq_gettext(" at character %s"),
 							  val);
 		}
 	}
@@ -947,7 +1007,7 @@ pqGetErrorNotice3(PGconn *conn, bool isError)
 		if (val)
 		{
 			querytext = PQresultErrorField(res, PG_DIAG_INTERNAL_QUERY);
-			if (conn->verbosity != PQERRORS_TERSE && querytext != NULL)
+			if (verbosity != PQERRORS_TERSE && querytext != NULL)
 			{
 				/* emit position as a syntax cursor display */
 				querypos = atoi(val);
@@ -956,59 +1016,59 @@ pqGetErrorNotice3(PGconn *conn, bool isError)
 			{
 				/* emit position as text addition to primary message */
 				/* translator: %s represents a digit string */
-				appendPQExpBuffer(&workBuf, libpq_gettext(" at character %s"),
+				appendPQExpBuffer(workBuf, libpq_gettext(" at character %s"),
 								  val);
 			}
 		}
 	}
-	appendPQExpBufferChar(&workBuf, '\n');
-	if (conn->verbosity != PQERRORS_TERSE)
+	appendPQExpBufferChar(workBuf, '\n');
+	if (verbosity != PQERRORS_TERSE)
 	{
 		if (querytext && querypos > 0)
-			reportErrorPosition(&workBuf, querytext, querypos,
+			reportErrorPosition(workBuf, querytext, querypos,
 								conn->client_encoding);
 		val = PQresultErrorField(res, PG_DIAG_MESSAGE_DETAIL);
 		if (val)
-			appendPQExpBuffer(&workBuf, libpq_gettext("DETAIL:  %s\n"), val);
+			appendPQExpBuffer(workBuf, libpq_gettext("DETAIL:  %s\n"), val);
 		val = PQresultErrorField(res, PG_DIAG_MESSAGE_HINT);
 		if (val)
-			appendPQExpBuffer(&workBuf, libpq_gettext("HINT:  %s\n"), val);
+			appendPQExpBuffer(workBuf, libpq_gettext("HINT:  %s\n"), val);
 		val = PQresultErrorField(res, PG_DIAG_INTERNAL_QUERY);
 		if (val)
-			appendPQExpBuffer(&workBuf, libpq_gettext("QUERY:  %s\n"), val);
+			appendPQExpBuffer(workBuf, libpq_gettext("QUERY:  %s\n"), val);
 		if (conn->show_context == PQSHOW_CONTEXT_ALWAYS ||
 			(conn->show_context == PQSHOW_CONTEXT_ERRORS && isError))
 		{
 			val = PQresultErrorField(res, PG_DIAG_CONTEXT);
 			if (val)
-				appendPQExpBuffer(&workBuf, libpq_gettext("CONTEXT:  %s\n"),
+				appendPQExpBuffer(workBuf, libpq_gettext("CONTEXT:  %s\n"),
 								  val);
 		}
 	}
-	if (conn->verbosity == PQERRORS_VERBOSE)
+	if (verbosity == PQERRORS_VERBOSE)
 	{
 		val = PQresultErrorField(res, PG_DIAG_SCHEMA_NAME);
 		if (val)
-			appendPQExpBuffer(&workBuf,
+			appendPQExpBuffer(workBuf,
 							  libpq_gettext("SCHEMA NAME:  %s\n"), val);
 		val = PQresultErrorField(res, PG_DIAG_TABLE_NAME);
 		if (val)
-			appendPQExpBuffer(&workBuf,
+			appendPQExpBuffer(workBuf,
 							  libpq_gettext("TABLE NAME:  %s\n"), val);
 		val = PQresultErrorField(res, PG_DIAG_COLUMN_NAME);
 		if (val)
-			appendPQExpBuffer(&workBuf,
+			appendPQExpBuffer(workBuf,
 							  libpq_gettext("COLUMN NAME:  %s\n"), val);
 		val = PQresultErrorField(res, PG_DIAG_DATATYPE_NAME);
 		if (val)
-			appendPQExpBuffer(&workBuf,
+			appendPQExpBuffer(workBuf,
 							  libpq_gettext("DATATYPE NAME:  %s\n"), val);
 		val = PQresultErrorField(res, PG_DIAG_CONSTRAINT_NAME);
 		if (val)
-			appendPQExpBuffer(&workBuf,
+			appendPQExpBuffer(workBuf,
 							  libpq_gettext("CONSTRAINT NAME:  %s\n"), val);
 	}
-	if (conn->verbosity == PQERRORS_VERBOSE)
+	if (verbosity == PQERRORS_VERBOSE)
 	{
 		const char *valf;
 		const char *vall;
@@ -1018,51 +1078,15 @@ pqGetErrorNotice3(PGconn *conn, bool isError)
 		val = PQresultErrorField(res, PG_DIAG_SOURCE_FUNCTION);
 		if (val || valf || vall)
 		{
-			appendPQExpBufferStr(&workBuf, libpq_gettext("LOCATION:  "));
+			appendPQExpBufferStr(workBuf, libpq_gettext("LOCATION:  "));
 			if (val)
-				appendPQExpBuffer(&workBuf, libpq_gettext("%s, "), val);
+				appendPQExpBuffer(workBuf, libpq_gettext("%s, "), val);
 			if (valf && vall)	/* unlikely we'd have just one */
-				appendPQExpBuffer(&workBuf, libpq_gettext("%s:%s"),
+				appendPQExpBuffer(workBuf, libpq_gettext("%s:%s"),
 								  valf, vall);
-			appendPQExpBufferChar(&workBuf, '\n');
+			appendPQExpBufferChar(workBuf, '\n');
 		}
 	}
-
-	/*
-	 * Either save error as current async result, or just emit the notice.
-	 */
-	if (isError)
-	{
-		if (res)
-			res->errMsg = pqResultStrdup(res, workBuf.data);
-		pqClearAsyncResult(conn);
-		conn->result = res;
-		if (PQExpBufferDataBroken(workBuf))
-			printfPQExpBuffer(&conn->errorMessage,
-							  libpq_gettext("out of memory"));
-		else
-			appendPQExpBufferStr(&conn->errorMessage, workBuf.data);
-	}
-	else
-	{
-		/* if we couldn't allocate the result set, just discard the NOTICE */
-		if (res)
-		{
-			/* We can cheat a little here and not copy the message. */
-			res->errMsg = workBuf.data;
-			if (res->noticeHooks.noticeRec != NULL)
-				(*res->noticeHooks.noticeRec) (res->noticeHooks.noticeRecArg, res);
-			PQclear(res);
-		}
-	}
-
-	termPQExpBuffer(&workBuf);
-	return 0;
-
-fail:
-	PQclear(res);
-	termPQExpBuffer(&workBuf);
-	return EOF;
 }
 
 /*
diff --git a/src/interfaces/libpq/libpq-fe.h b/src/interfaces/libpq/libpq-fe.h
index 6bf34b3..ad177cc 100644
--- a/src/interfaces/libpq/libpq-fe.h
+++ b/src/interfaces/libpq/libpq-fe.h
@@ -463,6 +463,7 @@ extern PGresult *PQfn(PGconn *conn,
 extern ExecStatusType PQresultStatus(const PGresult *res);
 extern char *PQresStatus(ExecStatusType status);
 extern char *PQresultErrorMessage(const PGresult *res);
+extern char *PQresultVerboseErrorMessage(const PGresult *res);
 extern char *PQresultErrorField(const PGresult *res, int fieldcode);
 extern int	PQntuples(const PGresult *res);
 extern int	PQnfields(const PGresult *res);
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 6c9bbf7..1574b7b 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -196,6 +196,7 @@ struct pg_result
 	 * per-field info then it is stored in a linked list.
 	 */
 	char	   *errMsg;			/* error message, or NULL if no error */
+	char	   *errMsgVerbose;	/* verbose error message, or NULL if no error */
 	PGMessageField *errFields;	/* message broken into fields */
 
 	/* All NULL attributes in the query result point to this null string */
@@ -539,7 +540,7 @@ extern pgthreadlock_t pg_g_threadlock;
 /* === in fe-exec.c === */
 
 extern void pqSetResultError(PGresult *res, const char *msg);
-extern void pqCatenateResultError(PGresult *res, const char *msg);
+extern void pqCatenateResultError(PGresult *res, const char *msg, const char *msgVerbose);
 extern void *pqResultAlloc(PGresult *res, size_t nBytes, bool isBinary);
 extern char *pqResultStrdup(PGresult *res, const char *str);
 extern void pqClearAsyncResult(PGconn *conn);
-- 
2.5.0

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