diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index 2328d8f5f21224a68b4dc4bf823159b8348920d2..3829a1400d93720812e9eb55adadda7326d4f106 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -2691,6 +2691,48 @@ char *PQresultErrorMessage(const PGresult *res);
       </listitem>
      </varlistentry>
 
+     <varlistentry id="libpq-pqresultverboseerrormessage">
+      <term>
+       <function>PQresultVerboseErrorMessage</function>
+       <indexterm>
+        <primary>PQresultVerboseErrorMessage</primary>
+       </indexterm>
+      </term>
+
+      <listitem>
+       <para>
+        Returns a reformatted version of the error message associated with
+        a <structname>PGresult</> object.
+<synopsis>
+char *PQresultVerboseErrorMessage(const PGresult *res,
+                                  PGVerbosity verbosity,
+                                  PGContextVisibility show_context);
+</synopsis>
+        In some situations a client might wish to obtain a more detailed
+        version of a previously-reported error.
+        <function>PQresultVerboseErrorMessage</function> addresses this need
+        by computing the message that would have been produced
+        by <function>PQresultErrorMessage</function> if the specified
+        verbosity settings had been in effect for the connection when the
+        given <structname>PGresult</> was generated.  If
+        the <structname>PGresult</> is not an error result,
+        <quote>PGresult is not an error result</> is reported instead.
+        The returned string includes a trailing newline.
+       </para>
+
+       <para>
+        Unlike most other functions for extracting data from
+        a <structname>PGresult</>, the result of this function is a freshly
+        allocated string.  The caller must free it
+        using <function>PQfreemem()</> when the string is no longer needed.
+       </para>
+
+       <para>
+        A NULL return is possible if there is insufficient memory.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="libpq-pqresulterrorfield">
       <term><function>PQresultErrorField</function><indexterm><primary>PQresultErrorField</></></term>
       <listitem>
@@ -5582,6 +5624,8 @@ PGVerbosity PQsetErrorVerbosity(PGconn *conn, PGVerbosity verbosity);
       mode includes all available fields.  Changing the verbosity does not
       affect the messages available from already-existing
       <structname>PGresult</> objects, only subsequently-created ones.
+      (But see <function>PQresultVerboseErrorMessage</function> if you
+      want to print a previous error with a different verbosity.)
      </para>
     </listitem>
    </varlistentry>
@@ -5622,6 +5666,8 @@ PGContextVisibility PQsetErrorContextVisibility(PGconn *conn, PGContextVisibilit
       affect the messages available from
       already-existing <structname>PGresult</> objects, only
       subsequently-created ones.
+      (But see <function>PQresultVerboseErrorMessage</function> if you
+      want to print a previous error with a different display mode.)
      </para>
     </listitem>
    </varlistentry>
@@ -6089,8 +6135,9 @@ PQsetNoticeProcessor(PGconn *conn,
    receiver function is called.  It is passed the message in the form of
    a <symbol>PGRES_NONFATAL_ERROR</symbol>
    <structname>PGresult</structname>.  (This allows the receiver to extract
-   individual fields using <function>PQresultErrorField</>, or the complete
-   preformatted message using <function>PQresultErrorMessage</>.) The same
+   individual fields using <function>PQresultErrorField</>, or obtain a
+   complete preformatted message using <function>PQresultErrorMessage</>
+   or <function>PQresultVerboseErrorMessage</>.)  The same
    void pointer passed to <function>PQsetNoticeReceiver</function> is also
    passed.  (This pointer can be used to access application-specific state
    if needed.)
diff --git a/src/interfaces/libpq/exports.txt b/src/interfaces/libpq/exports.txt
index c69a4d5ea4266d46bc4eb58f608c856f44b4fd95..21dd772ca919315b8dcdb22fb979107101bb81c6 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 41937c0bf9a5bdb6bf741df95f4cbb326ab923c9..2621767fd4adba9d1bf745e07181adb9780c9b19 100644
--- a/src/interfaces/libpq/fe-exec.c
+++ b/src/interfaces/libpq/fe-exec.c
@@ -159,6 +159,7 @@ PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status)
 	result->nEvents = 0;
 	result->errMsg = NULL;
 	result->errFields = NULL;
+	result->errQuery = NULL;
 	result->null_field[0] = '\0';
 	result->curBlock = NULL;
 	result->curOffset = 0;
@@ -2598,6 +2599,44 @@ PQresultErrorMessage(const PGresult *res)
 	return res->errMsg;
 }
 
+char *
+PQresultVerboseErrorMessage(const PGresult *res,
+							PGVerbosity verbosity,
+							PGContextVisibility show_context)
+{
+	PQExpBufferData workBuf;
+
+	/*
+	 * Because the caller is expected to free the result string, we must
+	 * strdup any constant result.  We use plain strdup and document that
+	 * callers should expect NULL if out-of-memory.
+	 */
+	if (!res ||
+		(res->resultStatus != PGRES_FATAL_ERROR &&
+		 res->resultStatus != PGRES_NONFATAL_ERROR))
+		return strdup(libpq_gettext("PGresult is not an error result\n"));
+
+	initPQExpBuffer(&workBuf);
+
+	/*
+	 * Currently, we pass this off to fe-protocol3.c in all cases; it will
+	 * behave reasonably sanely with an error reported by fe-protocol2.c as
+	 * well.  If necessary, we could record the protocol version in PGresults
+	 * so as to be able to invoke a version-specific message formatter, but
+	 * for now there's no need.
+	 */
+	pqBuildErrorMessage3(&workBuf, res, verbosity, show_context);
+
+	/* If insufficient memory to format the message, fail cleanly */
+	if (PQExpBufferDataBroken(workBuf))
+	{
+		termPQExpBuffer(&workBuf);
+		return strdup(libpq_gettext("out of memory\n"));
+	}
+
+	return workBuf.data;
+}
+
 char *
 PQresultErrorField(const PGresult *res, int fieldcode)
 {
diff --git a/src/interfaces/libpq/fe-protocol3.c b/src/interfaces/libpq/fe-protocol3.c
index 3034773972aaf9d3f31512eae9cb25ed2f898311..0b8c62f6ce297106c2602d2b361d29794fcf6ddf 100644
--- a/src/interfaces/libpq/fe-protocol3.c
+++ b/src/interfaces/libpq/fe-protocol3.c
@@ -876,11 +876,9 @@ int
 pqGetErrorNotice3(PGconn *conn, bool isError)
 {
 	PGresult   *res = NULL;
+	bool		have_position = false;
 	PQExpBufferData workBuf;
 	char		id;
-	const char *val;
-	const char *querytext = NULL;
-	int			querypos = 0;
 
 	/*
 	 * Since the fields might be pretty long, we create a temporary
@@ -905,6 +903,9 @@ pqGetErrorNotice3(PGconn *conn, bool isError)
 
 	/*
 	 * Read the fields and save into res.
+	 *
+	 * While at it, save the SQLSTATE in conn->last_sqlstate, and note whether
+	 * we saw a PG_DIAG_STATEMENT_POSITION field.
 	 */
 	for (;;)
 	{
@@ -915,42 +916,123 @@ pqGetErrorNotice3(PGconn *conn, bool isError)
 		if (pqGets(&workBuf, conn))
 			goto fail;
 		pqSaveMessageField(res, id, workBuf.data);
+		if (id == PG_DIAG_SQLSTATE)
+			strlcpy(conn->last_sqlstate, workBuf.data,
+					sizeof(conn->last_sqlstate));
+		else if (id == PG_DIAG_STATEMENT_POSITION)
+			have_position = true;
 	}
 
+	/*
+	 * Save the active query text, if any, into res as well; but only if we
+	 * might need it for an error cursor display, which is only true if there
+	 * is a PG_DIAG_STATEMENT_POSITION field.
+	 */
+	if (have_position && conn->last_query && res)
+		res->errQuery = pqResultStrdup(res, conn->last_query);
+
 	/*
 	 * Now build the "overall" error message for PQresultErrorMessage.
-	 *
-	 * Also, save the SQLSTATE in conn->last_sqlstate.
 	 */
 	resetPQExpBuffer(&workBuf);
+	pqBuildErrorMessage3(&workBuf, res, conn->verbosity, conn->show_context);
+
+	/*
+	 * 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;
+}
+
+/*
+ * Construct an error message from the fields in the given PGresult,
+ * appending it to the contents of "msg".
+ */
+void
+pqBuildErrorMessage3(PQExpBuffer msg, const PGresult *res,
+					 PGVerbosity verbosity, PGContextVisibility show_context)
+{
+	const char *val;
+	const char *querytext = NULL;
+	int			querypos = 0;
+
+	/* If we couldn't allocate a PGresult, just say "out of memory" */
+	if (res == NULL)
+	{
+		appendPQExpBuffer(msg, libpq_gettext("out of memory\n"));
+		return;
+	}
+
+	/*
+	 * If we don't have any broken-down fields, just return the base message.
+	 * This mainly applies if we're given a libpq-generated error result.
+	 */
+	if (res->errFields == NULL)
+	{
+		if (res->errMsg && res->errMsg[0])
+			appendPQExpBufferStr(msg, res->errMsg);
+		else
+			appendPQExpBuffer(msg, libpq_gettext("no error message available\n"));
+		return;
+	}
+
+	/* Else build error message from relevant fields */
 	val = PQresultErrorField(res, PG_DIAG_SEVERITY);
 	if (val)
-		appendPQExpBuffer(&workBuf, "%s:  ", val);
-	val = PQresultErrorField(res, PG_DIAG_SQLSTATE);
-	if (val)
+		appendPQExpBuffer(msg, "%s:  ", val);
+	if (verbosity == PQERRORS_VERBOSE)
 	{
-		if (strlen(val) < sizeof(conn->last_sqlstate))
-			strcpy(conn->last_sqlstate, val);
-		if (conn->verbosity == PQERRORS_VERBOSE)
-			appendPQExpBuffer(&workBuf, "%s: ", val);
+		val = PQresultErrorField(res, PG_DIAG_SQLSTATE);
+		if (val)
+			appendPQExpBuffer(msg, "%s: ", val);
 	}
 	val = PQresultErrorField(res, PG_DIAG_MESSAGE_PRIMARY);
 	if (val)
-		appendPQExpBufferStr(&workBuf, val);
+		appendPQExpBufferStr(msg, val);
 	val = PQresultErrorField(res, PG_DIAG_STATEMENT_POSITION);
 	if (val)
 	{
-		if (conn->verbosity != PQERRORS_TERSE && conn->last_query != NULL)
+		if (verbosity != PQERRORS_TERSE && res->errQuery != NULL)
 		{
 			/* emit position as a syntax cursor display */
-			querytext = conn->last_query;
+			querytext = res->errQuery;
 			querypos = atoi(val);
 		}
 		else
 		{
 			/* emit position as text addition to primary message */
 			/* translator: %s represents a digit string */
-			appendPQExpBuffer(&workBuf, libpq_gettext(" at character %s"),
+			appendPQExpBuffer(msg, libpq_gettext(" at character %s"),
 							  val);
 		}
 	}
@@ -960,7 +1042,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);
@@ -969,59 +1051,60 @@ 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(msg, libpq_gettext(" at character %s"),
 								  val);
 			}
 		}
 	}
-	appendPQExpBufferChar(&workBuf, '\n');
-	if (conn->verbosity != PQERRORS_TERSE)
+	appendPQExpBufferChar(msg, '\n');
+	if (verbosity != PQERRORS_TERSE)
 	{
 		if (querytext && querypos > 0)
-			reportErrorPosition(&workBuf, querytext, querypos,
-								conn->client_encoding);
+			reportErrorPosition(msg, querytext, querypos,
+								res->client_encoding);
 		val = PQresultErrorField(res, PG_DIAG_MESSAGE_DETAIL);
 		if (val)
-			appendPQExpBuffer(&workBuf, libpq_gettext("DETAIL:  %s\n"), val);
+			appendPQExpBuffer(msg, libpq_gettext("DETAIL:  %s\n"), val);
 		val = PQresultErrorField(res, PG_DIAG_MESSAGE_HINT);
 		if (val)
-			appendPQExpBuffer(&workBuf, libpq_gettext("HINT:  %s\n"), val);
+			appendPQExpBuffer(msg, libpq_gettext("HINT:  %s\n"), val);
 		val = PQresultErrorField(res, PG_DIAG_INTERNAL_QUERY);
 		if (val)
-			appendPQExpBuffer(&workBuf, libpq_gettext("QUERY:  %s\n"), val);
-		if (conn->show_context == PQSHOW_CONTEXT_ALWAYS ||
-			(conn->show_context == PQSHOW_CONTEXT_ERRORS && isError))
+			appendPQExpBuffer(msg, libpq_gettext("QUERY:  %s\n"), val);
+		if (show_context == PQSHOW_CONTEXT_ALWAYS ||
+			(show_context == PQSHOW_CONTEXT_ERRORS &&
+			 res->resultStatus == PGRES_FATAL_ERROR))
 		{
 			val = PQresultErrorField(res, PG_DIAG_CONTEXT);
 			if (val)
-				appendPQExpBuffer(&workBuf, libpq_gettext("CONTEXT:  %s\n"),
+				appendPQExpBuffer(msg, 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(msg,
 							  libpq_gettext("SCHEMA NAME:  %s\n"), val);
 		val = PQresultErrorField(res, PG_DIAG_TABLE_NAME);
 		if (val)
-			appendPQExpBuffer(&workBuf,
+			appendPQExpBuffer(msg,
 							  libpq_gettext("TABLE NAME:  %s\n"), val);
 		val = PQresultErrorField(res, PG_DIAG_COLUMN_NAME);
 		if (val)
-			appendPQExpBuffer(&workBuf,
+			appendPQExpBuffer(msg,
 							  libpq_gettext("COLUMN NAME:  %s\n"), val);
 		val = PQresultErrorField(res, PG_DIAG_DATATYPE_NAME);
 		if (val)
-			appendPQExpBuffer(&workBuf,
+			appendPQExpBuffer(msg,
 							  libpq_gettext("DATATYPE NAME:  %s\n"), val);
 		val = PQresultErrorField(res, PG_DIAG_CONSTRAINT_NAME);
 		if (val)
-			appendPQExpBuffer(&workBuf,
+			appendPQExpBuffer(msg,
 							  libpq_gettext("CONSTRAINT NAME:  %s\n"), val);
 	}
-	if (conn->verbosity == PQERRORS_VERBOSE)
+	if (verbosity == PQERRORS_VERBOSE)
 	{
 		const char *valf;
 		const char *vall;
@@ -1031,51 +1114,15 @@ pqGetErrorNotice3(PGconn *conn, bool isError)
 		val = PQresultErrorField(res, PG_DIAG_SOURCE_FUNCTION);
 		if (val || valf || vall)
 		{
-			appendPQExpBufferStr(&workBuf, libpq_gettext("LOCATION:  "));
+			appendPQExpBufferStr(msg, libpq_gettext("LOCATION:  "));
 			if (val)
-				appendPQExpBuffer(&workBuf, libpq_gettext("%s, "), val);
+				appendPQExpBuffer(msg, libpq_gettext("%s, "), val);
 			if (valf && vall)	/* unlikely we'd have just one */
-				appendPQExpBuffer(&workBuf, libpq_gettext("%s:%s"),
+				appendPQExpBuffer(msg, libpq_gettext("%s:%s"),
 								  valf, vall);
-			appendPQExpBufferChar(&workBuf, '\n');
+			appendPQExpBufferChar(msg, '\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 6bf34b3e99586798ab1c5ff8f424365cd64ccda1..9ca0756c4bfaa7e0ecf256656f5e6f0ee8214515 100644
--- a/src/interfaces/libpq/libpq-fe.h
+++ b/src/interfaces/libpq/libpq-fe.h
@@ -463,6 +463,9 @@ 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,
+							PGVerbosity verbosity,
+							PGContextVisibility show_context);
 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 6c9bbf77608bd2d6e4925e55d5317a0c1732da0d..1183323a4456a84184d7fdbedc6e7d00aed928fe 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -197,6 +197,7 @@ struct pg_result
 	 */
 	char	   *errMsg;			/* error message, or NULL if no error */
 	PGMessageField *errFields;	/* message broken into fields */
+	char	   *errQuery;		/* text of triggering query, if available */
 
 	/* All NULL attributes in the query result point to this null string */
 	char		null_field[1];
@@ -575,6 +576,8 @@ extern char *pqBuildStartupPacket3(PGconn *conn, int *packetlen,
 					  const PQEnvironmentOption *options);
 extern void pqParseInput3(PGconn *conn);
 extern int	pqGetErrorNotice3(PGconn *conn, bool isError);
+extern void pqBuildErrorMessage3(PQExpBuffer msg, const PGresult *res,
+					 PGVerbosity verbosity, PGContextVisibility show_context);
 extern int	pqGetCopyData3(PGconn *conn, char **buffer, int async);
 extern int	pqGetline3(PGconn *conn, char *s, int maxlen);
 extern int	pqGetlineAsync3(PGconn *conn, char *buffer, int bufsize);