Thread: Multiple hosts in connection string failed to failover in non-hot standby mode

Multiple hosts in connection string failed to failover in non-hot standby mode

From
Hubert Zhang
Date:
Hi hackers,

Libpq has supported to specify multiple hosts in connection string and enable auto failover when the previous PostgreSQL instance cannot be accessed.
But when I tried to enable this feature for a non-hot standby, it cannot do the failover with the following messages.

psql: error: could not connect to server: FATAL:  the database system is starting up

Document says ' If a connection is established successfully, but authentication fails, the remaining hosts in the list are not tried.
I'm wondering is it a feature by design or a bug? If it's a bug, I plan to fix it.

Thanks,
Hubert Zhang

RE: Multiple hosts in connection string failed to failover in non-hot standby mode

From
"tsunakawa.takay@fujitsu.com"
Date:
Please send emails in text format.  Your email was in HTML, and I changed this reply to text format.


From: Hubert Zhang <zhubert@vmware.com>
> Libpq has supported to specify multiple hosts in connection string and enable auto failover when the previous
PostgreSQLinstance cannot be accessed. 
> But when I tried to enable this feature for a non-hot standby, it cannot do the failover with the following messages.
>
> psql: error: could not connect to server: FATAL:  the database system is starting up

Was the primary running and accepting connections when you encountered this error?  That is, if you specified
host="host1host2", host1 was the non-hot standby and host2 was a running primary?  Or only the non-hot standby was
running?

If a primary was running, I'd say it's a bug...  Perhaps the following part in libpq gives up connection attempts wen
theabove FATAL error is returned from the server.  Maybe libpq should differentiate errors using SQLSTATE and continue
connectionattempts on other hosts. 

[fe-connect.c]
                /* Handle errors. */
                if (beresp == 'E')
                {
                    if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
...
#endif

                    goto error_return;
                }

                /* It is an authentication request. */
                conn->auth_req_received = true;

                /* Get the type of request. */


Regards
Takayuki Tsunakawa




On Tue, Oct 27, 2020 at 07:14:14AM +0000, Hubert Zhang wrote:
> Libpq has supported to specify multiple hosts in connection string and enable auto failover when the previous
PostgreSQLinstance cannot be accessed.
 
> But when I tried to enable this feature for a non-hot standby, it cannot do the failover with the following
messages.
> 
> psql: error: could not connect to server: FATAL:  the database system is starting up
> 
> Document says ' If a connection is established successfully, but authentication fails, the remaining hosts in the
listare not tried.'
 
> I'm wondering is it a feature by design or a bug? If it's a bug, I plan to fix it.

I felt it was a bug, but the community as a whole may or may not agree:
https://postgr.es/m/flat/16508-1a63222835164566%40postgresql.org



Was the primary running and accepting connections when you encountered this error?  That is, if you specified host="host1 host2", host1 was the non-hot standby and host2 was a running primary?  Or only the non-hot standby was running?

If a primary was running, I'd say it's a bug...  Perhaps the following part in libpq gives up connection attempts wen the above FATAL error is returned from the server.  Maybe libpq should differentiate errors using SQLSTATE and continue connection attempts on other hosts.
Yes, the primary was running, but non-hot standby is in front of the primary in connection string.
Hao Wu and I wrote a patch to fix this problem. Client side libpq should try another hosts in connection string when it is rejected by a non-hot standby, or the first host encounter some n/w problems during the libpq handshake.

Please send emails in text format.  Your email was in HTML, and I changed this reply to text format.
Thanks. Is this email in text format now? I just use outlook in chrome. Let me know if it still in html format.

Hubert & Hao Wu
 

From: tsunakawa.takay@fujitsu.com <tsunakawa.takay@fujitsu.com>
Sent: Tuesday, October 27, 2020 5:30 PM
To: Hubert Zhang <zhubert@vmware.com>
Cc: pgsql-hackers@postgresql.org <pgsql-hackers@postgresql.org>
Subject: RE: Multiple hosts in connection string failed to failover in non-hot standby mode
 
Please send emails in text format.  Your email was in HTML, and I changed this reply to text format.


From: Hubert Zhang <zhubert@vmware.com>
> Libpq has supported to specify multiple hosts in connection string and enable auto failover when the previous PostgreSQL instance cannot be accessed.
> But when I tried to enable this feature for a non-hot standby, it cannot do the failover with the following messages.
>
> psql: error: could not connect to server: FATAL:  the database system is starting up

Was the primary running and accepting connections when you encountered this error?  That is, if you specified host="host1 host2", host1 was the non-hot standby and host2 was a running primary?  Or only the non-hot standby was running?

If a primary was running, I'd say it's a bug...  Perhaps the following part in libpq gives up connection attempts wen the above FATAL error is returned from the server.  Maybe libpq should differentiate errors using SQLSTATE and continue connection attempts on other hosts.

[fe-connect.c]
                /* Handle errors. */
                if (beresp == 'E')
                {
                    if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
...
#endif

                    goto error_return;
                }

                /* It is an authentication request. */
                conn->auth_req_received = true;

                /* Get the type of request. */


Regards
Takayuki Tsunakawa

Attachment

RE: Multiple hosts in connection string failed to failover in non-hot standby mode

From
"tsunakawa.takay@fujitsu.com"
Date:
From: Hubert Zhang <zhubert@vmware.com>
> Hao Wu and I wrote a patch to fix this problem. Client side libpq should try another hosts in connection string when
itis rejected by a non-hot standby, or the first host encounter some n/w problems during the libpq handshake. 

Thank you.  Please add it to the November Commitfest.


> Thanks. Is this email in text format now? I just use outlook in chrome. Let me know if it still in html format.

I'm afraid not.  The Outlook's title bar says that it's in HTML format.  I'm using Outlook 2016 client app on Windows
10. I may have failed to convert my previous email to text, but it should be text format this time. 


Regards
Takayuki Tsunakawa




Hubert Zhang <zhubert@vmware.com> writes:
> [ 0001-Enhance-libpq-to-support-multiple-host-for-non-hot-s.patch ]

I took a quick look at this.  TBH, I'd just drop the first three hunks,
as they've got nothing to do with any failure mode that there's evidence
for in this thread or the prior one, and I'm afraid they're more likely
to create trouble than fix it.

As for the last hunk, why is it after rather than before the SSL/GSS
checks?  I doubt that retrying with/without SSL is going to change
a CANNOT_CONNECT_NOW result, unless maybe by slowing things down to
the point where recovery has finished ;-)

The bigger picture though is

(1) what set of failures should we retry on?  I think CANNOT_CONNECT_NOW
is reasonable, but are there others?

(2) what does this do to the quality of the error messages in cases
where all the connection attempts fail?

I think that error message quality was not thought too much about
in the original development of the multi-host feature, so to some
extent I'm asking you to clean up someone else's mess.  Nonetheless,
I feel that we do need to clean it up before we do things that risk
making it even more confusing.

The problems that I see in this area are first that there's no
real standardization in libpq as to whether to append error messages
together or just flush preceding messages; and second that no effort
is made in multi-connection-attempt cases to separate the errors from
different attempts, much less identify which error goes with which
host or IP address.  I think we really need to put some work into
that.  In some cases you can infer what happened from breadcrumbs
we already put into the text, for example

$ psql -h localhost,/tmp -p 12345
psql: error: could not connect to server: Connection refused
        Is the server running on host "localhost" (::1) and accepting
        TCP/IP connections on port 12345?
could not connect to server: Connection refused
        Is the server running on host "localhost" (127.0.0.1) and accepting
        TCP/IP connections on port 12345?
could not connect to server: No such file or directory
        Is the server running locally and accepting
        connections on Unix domain socket "/tmp/.s.PGSQL.12345"?

but this doesn't seem particularly helpfully laid out to me, and we don't
provide the breadcrumbs at all for a lot of other error cases.

I'm vaguely imagining that we could do something more like

could not connect to host "localhost" (::1), port 12345: Connection refused
could not connect to host "localhost" (127.0.0.1), port 12345: Connection refused
could not connect to socket "/tmp/.s.PGSQL.12345": No such file or directory

Not quite sure if the "Is the server running" hint is worth preserving.
We'd have to reword it quite a bit, and it'd be very duplicative.

The implementation of this might involve sticking the initial string
(up to the colon, in this example) into conn->errorMessage speculatively
as we try each host.  If we then append an error to it and go around
again, we're good.  If we successfully connect, then the contents of
conn->errorMessage don't matter.

            regards, tom lane



I wrote:
> The problems that I see in this area are first that there's no
> real standardization in libpq as to whether to append error messages
> together or just flush preceding messages; and second that no effort
> is made in multi-connection-attempt cases to separate the errors from
> different attempts, much less identify which error goes with which
> host or IP address.  I think we really need to put some work into
> that.

I spent some time on this, and here is a patch set that tries to
improve matters.

0001 changes the libpq coding rules to be that all error messages should
be appended to conn->errorMessage, never overwritten (there are a couple
of exceptions in fe-lobj.c) and we reset conn->errorMessage to empty only
at the start of a connection request or new query.  This is something
that's been bugging me for a long time and I'm glad to get it cleaned up.
Formerly it seemed that a dartboard had been used to decide whether to use
"printfPQExpBuffer" or "appendPQExpBuffer"; now it's always the latter.
We can also get rid of several hacks that were used to get around the
mess and force appending behavior.

0002 then changes the reporting rules in fe-connect.c as I suggested,
so that you might get errors like this:

$ psql -h localhost,/tmp -p 12345
psql: error: could not connect to host "localhost" (::1), port 12345: Connection refused
        Is the server running on that host and accepting TCP/IP connections?
could not connect to host "localhost" (127.0.0.1), port 12345: Connection refused
        Is the server running on that host and accepting TCP/IP connections?
could not connect to socket "/tmp/.s.PGSQL.12345": No such file or directory
        Is the server running locally and accepting connections on that socket?

and we have a pretty uniform rule that errors coming back from a
connection attempt will be prefixed with the server address.

Then 0003 is the part of your patch that I'm happy with.  Given 0001+0002
we could certainly consider looping back and retrying for more cases, but
I still want to tread lightly on that.  I don't see a lot of value in
retrying errors that seem to be on the client side, such as failure to
set socket properties; and in general I'm hesitant to add untestable
code paths here.

I feel pretty good about 0001: it might be committable as-is.  0002 is
probably subject to bikeshedding, plus it has a problem in the ECPG tests.
Two of the error messages are now unstable because they expose
chosen-at-random socket paths:

diff -U3 /home/postgres/pgsql/src/interfaces/ecpg/test/expected/connect-test5.stderr
/home/postgres/pgsql/src/interfaces/ecpg/test/results/connect-test5.stderr
--- /home/postgres/pgsql/src/interfaces/ecpg/test/expected/connect-test5.stderr 2020-08-04 14:59:45.617380050 -0400
+++ /home/postgres/pgsql/src/interfaces/ecpg/test/results/connect-test5.stderr  2021-01-10 16:12:22.539433702 -0500
@@ -36,7 +36,7 @@
 [NO_PID]: sqlca: code: 0, state: 00000
 [NO_PID]: ECPGconnect: opening database <DEFAULT> on <DEFAULT> port <DEFAULT>  for user regress_ecpg_user2
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ECPGconnect: could not open database: FATAL:  database "regress_ecpg_user2" does not exist
+[NO_PID]: ECPGconnect: could not open database: could not connect to socket "/tmp/pg_regress-EbHubF/.s.PGSQL.58080":
FATAL: database "regress_ecpg_user2" does not exist 

 [NO_PID]: sqlca: code: 0, state: 00000
 [NO_PID]: ecpg_finish: connection main closed
@@ -73,7 +73,7 @@
 [NO_PID]: sqlca: code: -220, state: 08003
 [NO_PID]: ECPGconnect: opening database <DEFAULT> on <DEFAULT> port <DEFAULT>  for user regress_ecpg_user2
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ECPGconnect: could not open database: FATAL:  database "regress_ecpg_user2" does not exist
+[NO_PID]: ECPGconnect: could not open database: could not connect to socket "/tmp/pg_regress-EbHubF/.s.PGSQL.58080":
FATAL: database "regress_ecpg_user2" does not exist 

 [NO_PID]: sqlca: code: 0, state: 00000
 [NO_PID]: ecpg_finish: connection main closed

I don't have any non-hacky ideas what to do about that.  The extra detail
seems useful to end users, but we don't have any infrastructure that
would allow filtering it out in the ECPG tests.

            regards, tom lane

diff --git a/src/interfaces/libpq/fe-auth-scram.c b/src/interfaces/libpq/fe-auth-scram.c
index b76f0befd0..8b60378379 100644
--- a/src/interfaces/libpq/fe-auth-scram.c
+++ b/src/interfaces/libpq/fe-auth-scram.c
@@ -208,13 +208,13 @@ pg_fe_scram_exchange(void *opaq, char *input, int inputlen,
     {
         if (inputlen == 0)
         {
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("malformed SCRAM message (empty message)\n"));
             goto error;
         }
         if (inputlen != strlen(input))
         {
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("malformed SCRAM message (length mismatch)\n"));
             goto error;
         }
@@ -258,14 +258,14 @@ pg_fe_scram_exchange(void *opaq, char *input, int inputlen,
              */
             if (!verify_server_signature(state, success))
             {
-                printfPQExpBuffer(&conn->errorMessage,
+                appendPQExpBuffer(&conn->errorMessage,
                                   libpq_gettext("could not verify server signature\n"));
                 goto error;
             }

             if (!*success)
             {
-                printfPQExpBuffer(&conn->errorMessage,
+                appendPQExpBuffer(&conn->errorMessage,
                                   libpq_gettext("incorrect server signature\n"));
             }
             *done = true;
@@ -274,7 +274,7 @@ pg_fe_scram_exchange(void *opaq, char *input, int inputlen,

         default:
             /* shouldn't happen */
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("invalid SCRAM exchange state\n"));
             goto error;
     }
@@ -287,6 +287,11 @@ error:

 /*
  * Read value for an attribute part of a SCRAM message.
+ *
+ * The buffer at **input is destructively modified, and *input is
+ * advanced over the "attr=value" string and any following comma.
+ *
+ * On failure, append an error message to *errorMessage and return NULL.
  */
 static char *
 read_attr_value(char **input, char attr, PQExpBuffer errorMessage)
@@ -296,7 +301,7 @@ read_attr_value(char **input, char attr, PQExpBuffer errorMessage)

     if (*begin != attr)
     {
-        printfPQExpBuffer(errorMessage,
+        appendPQExpBuffer(errorMessage,
                           libpq_gettext("malformed SCRAM message (attribute \"%c\" expected)\n"),
                           attr);
         return NULL;
@@ -305,7 +310,7 @@ read_attr_value(char **input, char attr, PQExpBuffer errorMessage)

     if (*begin != '=')
     {
-        printfPQExpBuffer(errorMessage,
+        appendPQExpBuffer(errorMessage,
                           libpq_gettext("malformed SCRAM message (expected character \"=\" for attribute \"%c\")\n"),
                           attr);
         return NULL;
@@ -346,7 +351,7 @@ build_client_first_message(fe_scram_state *state)
      */
     if (!pg_strong_random(raw_nonce, SCRAM_RAW_NONCE_LEN))
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("could not generate nonce\n"));
         return NULL;
     }
@@ -356,7 +361,7 @@ build_client_first_message(fe_scram_state *state)
     state->client_nonce = malloc(encoded_len + 1);
     if (state->client_nonce == NULL)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("out of memory\n"));
         return NULL;
     }
@@ -364,7 +369,7 @@ build_client_first_message(fe_scram_state *state)
                                 state->client_nonce, encoded_len);
     if (encoded_len < 0)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("could not encode nonce\n"));
         return NULL;
     }
@@ -431,7 +436,7 @@ build_client_first_message(fe_scram_state *state)

 oom_error:
     termPQExpBuffer(&buf);
-    printfPQExpBuffer(&conn->errorMessage,
+    appendPQExpBuffer(&conn->errorMessage,
                       libpq_gettext("out of memory\n"));
     return NULL;
 }
@@ -508,7 +513,7 @@ build_client_final_message(fe_scram_state *state)
             free(cbind_data);
             free(cbind_input);
             termPQExpBuffer(&buf);
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               "could not encode cbind data for channel binding\n");
             return NULL;
         }
@@ -523,7 +528,7 @@ build_client_final_message(fe_scram_state *state)
          * Shouldn't happen.
          */
         termPQExpBuffer(&buf);
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           "channel binding not supported by this build\n");
         return NULL;
 #endif                            /* HAVE_PGTLS_GET_PEER_CERTIFICATE_HASH */
@@ -553,7 +558,7 @@ build_client_final_message(fe_scram_state *state)
                                 client_proof))
     {
         termPQExpBuffer(&buf);
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("could not calculate client proof\n"));
         return NULL;
     }
@@ -569,7 +574,7 @@ build_client_final_message(fe_scram_state *state)
     if (encoded_len < 0)
     {
         termPQExpBuffer(&buf);
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("could not encode client proof\n"));
         return NULL;
     }
@@ -585,7 +590,7 @@ build_client_final_message(fe_scram_state *state)

 oom_error:
     termPQExpBuffer(&buf);
-    printfPQExpBuffer(&conn->errorMessage,
+    appendPQExpBuffer(&conn->errorMessage,
                       libpq_gettext("out of memory\n"));
     return NULL;
 }
@@ -606,7 +611,7 @@ read_server_first_message(fe_scram_state *state, char *input)
     state->server_first_message = strdup(input);
     if (state->server_first_message == NULL)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("out of memory\n"));
         return false;
     }
@@ -616,7 +621,7 @@ read_server_first_message(fe_scram_state *state, char *input)
                             &conn->errorMessage);
     if (nonce == NULL)
     {
-        /* read_attr_value() has generated an error string */
+        /* read_attr_value() has appended an error string */
         return false;
     }

@@ -624,7 +629,7 @@ read_server_first_message(fe_scram_state *state, char *input)
     if (strlen(nonce) < strlen(state->client_nonce) ||
         memcmp(nonce, state->client_nonce, strlen(state->client_nonce)) != 0)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("invalid SCRAM response (nonce mismatch)\n"));
         return false;
     }
@@ -632,7 +637,7 @@ read_server_first_message(fe_scram_state *state, char *input)
     state->nonce = strdup(nonce);
     if (state->nonce == NULL)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("out of memory\n"));
         return false;
     }
@@ -640,14 +645,14 @@ read_server_first_message(fe_scram_state *state, char *input)
     encoded_salt = read_attr_value(&input, 's', &conn->errorMessage);
     if (encoded_salt == NULL)
     {
-        /* read_attr_value() has generated an error string */
+        /* read_attr_value() has appended an error string */
         return false;
     }
     decoded_salt_len = pg_b64_dec_len(strlen(encoded_salt));
     state->salt = malloc(decoded_salt_len);
     if (state->salt == NULL)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("out of memory\n"));
         return false;
     }
@@ -657,7 +662,7 @@ read_server_first_message(fe_scram_state *state, char *input)
                                    decoded_salt_len);
     if (state->saltlen < 0)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("malformed SCRAM message (invalid salt)\n"));
         return false;
     }
@@ -665,19 +670,19 @@ read_server_first_message(fe_scram_state *state, char *input)
     iterations_str = read_attr_value(&input, 'i', &conn->errorMessage);
     if (iterations_str == NULL)
     {
-        /* read_attr_value() has generated an error string */
+        /* read_attr_value() has appended an error string */
         return false;
     }
     state->iterations = strtol(iterations_str, &endptr, 10);
     if (*endptr != '\0' || state->iterations < 1)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("malformed SCRAM message (invalid iteration count)\n"));
         return false;
     }

     if (*input != '\0')
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("malformed SCRAM message (garbage at end of server-first-message)\n"));

     return true;
@@ -697,7 +702,7 @@ read_server_final_message(fe_scram_state *state, char *input)
     state->server_final_message = strdup(input);
     if (!state->server_final_message)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("out of memory\n"));
         return false;
     }
@@ -708,7 +713,12 @@ read_server_final_message(fe_scram_state *state, char *input)
         char       *errmsg = read_attr_value(&input, 'e',
                                              &conn->errorMessage);

-        printfPQExpBuffer(&conn->errorMessage,
+        if (errmsg == NULL)
+        {
+            /* read_attr_value() has appended an error message */
+            return false;
+        }
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("error received from server in SCRAM exchange: %s\n"),
                           errmsg);
         return false;
@@ -719,19 +729,19 @@ read_server_final_message(fe_scram_state *state, char *input)
                                                &conn->errorMessage);
     if (encoded_server_signature == NULL)
     {
-        /* read_attr_value() has generated an error message */
+        /* read_attr_value() has appended an error message */
         return false;
     }

     if (*input != '\0')
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("malformed SCRAM message (garbage at end of server-final-message)\n"));

     server_signature_len = pg_b64_dec_len(strlen(encoded_server_signature));
     decoded_server_signature = malloc(server_signature_len);
     if (!decoded_server_signature)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("out of memory\n"));
         return false;
     }
@@ -743,7 +753,7 @@ read_server_final_message(fe_scram_state *state, char *input)
     if (server_signature_len != SCRAM_KEY_LEN)
     {
         free(decoded_server_signature);
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("malformed SCRAM message (invalid server signature)\n"));
         return false;
     }
diff --git a/src/interfaces/libpq/fe-auth.c b/src/interfaces/libpq/fe-auth.c
index a25fe4dd17..089226dc8a 100644
--- a/src/interfaces/libpq/fe-auth.c
+++ b/src/interfaces/libpq/fe-auth.c
@@ -72,7 +72,7 @@ pg_GSS_continue(PGconn *conn, int payloadlen)
         ginbuf.value = malloc(payloadlen);
         if (!ginbuf.value)
         {
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("out of memory allocating GSSAPI buffer (%d)\n"),
                               payloadlen);
             return STATUS_ERROR;
@@ -154,14 +154,14 @@ pg_GSS_startup(PGconn *conn, int payloadlen)

     if (!(host && host[0] != '\0'))
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("host name must be specified\n"));
         return STATUS_ERROR;
     }

     if (conn->gctx)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("duplicate GSS authentication request\n"));
         return STATUS_ERROR;
     }
@@ -195,10 +195,10 @@ pg_SSPI_error(PGconn *conn, const char *mprefix, SECURITY_STATUS r)
                       FORMAT_MESSAGE_FROM_SYSTEM,
                       NULL, r, 0,
                       sysmsg, sizeof(sysmsg), NULL) == 0)
-        printfPQExpBuffer(&conn->errorMessage, "%s: SSPI error %x\n",
+        appendPQExpBuffer(&conn->errorMessage, "%s: SSPI error %x\n",
                           mprefix, (unsigned int) r);
     else
-        printfPQExpBuffer(&conn->errorMessage, "%s: %s (%x)\n",
+        appendPQExpBuffer(&conn->errorMessage, "%s: %s (%x)\n",
                           mprefix, sysmsg, (unsigned int) r);
 }

@@ -226,7 +226,7 @@ pg_SSPI_continue(PGconn *conn, int payloadlen)
         inputbuf = malloc(payloadlen);
         if (!inputbuf)
         {
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("out of memory allocating SSPI buffer (%d)\n"),
                               payloadlen);
             return STATUS_ERROR;
@@ -286,7 +286,7 @@ pg_SSPI_continue(PGconn *conn, int payloadlen)
         conn->sspictx = malloc(sizeof(CtxtHandle));
         if (conn->sspictx == NULL)
         {
-            printfPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory\n"));
+            appendPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory\n"));
             return STATUS_ERROR;
         }
         memcpy(conn->sspictx, &newContext, sizeof(CtxtHandle));
@@ -305,7 +305,7 @@ pg_SSPI_continue(PGconn *conn, int payloadlen)
              * authentication. Keep check in case it shows up with other
              * authentication methods later.
              */
-            printfPQExpBuffer(&conn->errorMessage, "SSPI returned invalid number of output buffers\n");
+            appendPQExpBuffer(&conn->errorMessage, "SSPI returned invalid number of output buffers\n");
             return STATUS_ERROR;
         }

@@ -345,7 +345,7 @@ pg_SSPI_startup(PGconn *conn, int use_negotiate, int payloadlen)

     if (conn->sspictx)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("duplicate SSPI authentication request\n"));
         return STATUS_ERROR;
     }
@@ -356,7 +356,7 @@ pg_SSPI_startup(PGconn *conn, int use_negotiate, int payloadlen)
     conn->sspicred = malloc(sizeof(CredHandle));
     if (conn->sspicred == NULL)
     {
-        printfPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory\n"));
+        appendPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory\n"));
         return STATUS_ERROR;
     }

@@ -384,14 +384,14 @@ pg_SSPI_startup(PGconn *conn, int use_negotiate, int payloadlen)
      */
     if (!(host && host[0] != '\0'))
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("host name must be specified\n"));
         return STATUS_ERROR;
     }
     conn->sspitarget = malloc(strlen(conn->krbsrvname) + strlen(host) + 2);
     if (!conn->sspitarget)
     {
-        printfPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory\n"));
+        appendPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory\n"));
         return STATUS_ERROR;
     }
     sprintf(conn->sspitarget, "%s/%s", conn->krbsrvname, host);
@@ -425,14 +425,14 @@ pg_SASL_init(PGconn *conn, int payloadlen)
     if (conn->channel_binding[0] == 'r' &&    /* require */
         !conn->ssl_in_use)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("channel binding required, but SSL not in use\n"));
         goto error;
     }

     if (conn->sasl_state)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("duplicate SASL authentication request\n"));
         goto error;
     }
@@ -448,7 +448,7 @@ pg_SASL_init(PGconn *conn, int payloadlen)
     {
         if (pqGets(&mechanism_buf, conn))
         {
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               "fe_sendauth: invalid authentication request from server: invalid list of authentication
mechanisms\n");
             goto error;
         }
@@ -488,7 +488,7 @@ pg_SASL_init(PGconn *conn, int payloadlen)
                  */
                 if (conn->channel_binding[0] == 'r')    /* require */
                 {
-                    printfPQExpBuffer(&conn->errorMessage,
+                    appendPQExpBuffer(&conn->errorMessage,
                                       libpq_gettext("channel binding is required, but client does not support it\n"));
                     goto error;
                 }
@@ -505,7 +505,7 @@ pg_SASL_init(PGconn *conn, int payloadlen)
                  * the client and server supported it. The SCRAM exchange
                  * checks for that, to prevent downgrade attacks.
                  */
-                printfPQExpBuffer(&conn->errorMessage,
+                appendPQExpBuffer(&conn->errorMessage,
                                   libpq_gettext("server offered SCRAM-SHA-256-PLUS authentication over a non-SSL
connection\n"));
                 goto error;
             }
@@ -517,7 +517,7 @@ pg_SASL_init(PGconn *conn, int payloadlen)

     if (!selected_mechanism)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("none of the server's SASL authentication mechanisms are supported\n"));
         goto error;
     }
@@ -525,7 +525,7 @@ pg_SASL_init(PGconn *conn, int payloadlen)
     if (conn->channel_binding[0] == 'r' &&    /* require */
         strcmp(selected_mechanism, SCRAM_SHA_256_PLUS_NAME) != 0)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("channel binding is required, but server did not offer an authentication
methodthat supports channel binding\n")); 
         goto error;
     }
@@ -546,7 +546,7 @@ pg_SASL_init(PGconn *conn, int payloadlen)
         password = conn->pgpass;
     if (password == NULL || password[0] == '\0')
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           PQnoPasswordSupplied);
         goto error;
     }
@@ -607,7 +607,7 @@ oom_error:
     termPQExpBuffer(&mechanism_buf);
     if (initialresponse)
         free(initialresponse);
-    printfPQExpBuffer(&conn->errorMessage,
+    appendPQExpBuffer(&conn->errorMessage,
                       libpq_gettext("out of memory\n"));
     return STATUS_ERROR;
 }
@@ -631,7 +631,7 @@ pg_SASL_continue(PGconn *conn, int payloadlen, bool final)
     challenge = malloc(payloadlen + 1);
     if (!challenge)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("out of memory allocating SASL buffer (%d)\n"),
                           payloadlen);
         return STATUS_ERROR;
@@ -656,7 +656,7 @@ pg_SASL_continue(PGconn *conn, int payloadlen, bool final)
         if (outputlen != 0)
             free(output);

-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("AuthenticationSASLFinal received from server, but SASL authentication was not
completed\n"));
         return STATUS_ERROR;
     }
@@ -726,14 +726,14 @@ pg_local_sendauth(PGconn *conn)
     {
         char        sebuf[PG_STRERROR_R_BUFLEN];

-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           "pg_local_sendauth: sendmsg: %s\n",
                           strerror_r(errno, sebuf, sizeof(sebuf)));
         return STATUS_ERROR;
     }
     return STATUS_OK;
 #else
-    printfPQExpBuffer(&conn->errorMessage,
+    appendPQExpBuffer(&conn->errorMessage,
                       libpq_gettext("SCM_CRED authentication method not supported\n"));
     return STATUS_ERROR;
 #endif
@@ -766,7 +766,7 @@ pg_password_sendauth(PGconn *conn, const char *password, AuthRequest areq)
                 crypt_pwd = malloc(2 * (MD5_PASSWD_LEN + 1));
                 if (!crypt_pwd)
                 {
-                    printfPQExpBuffer(&conn->errorMessage,
+                    appendPQExpBuffer(&conn->errorMessage,
                                       libpq_gettext("out of memory\n"));
                     return STATUS_ERROR;
                 }
@@ -832,13 +832,13 @@ check_expected_areq(AuthRequest areq, PGconn *conn)
             case AUTH_REQ_OK:
                 if (!pg_fe_scram_channel_bound(conn->sasl_state))
                 {
-                    printfPQExpBuffer(&conn->errorMessage,
+                    appendPQExpBuffer(&conn->errorMessage,
                                       libpq_gettext("channel binding required, but server authenticated client without
channelbinding\n")); 
                     result = false;
                 }
                 break;
             default:
-                printfPQExpBuffer(&conn->errorMessage,
+                appendPQExpBuffer(&conn->errorMessage,
                                   libpq_gettext("channel binding required but not supported by server's authentication
request\n"));
                 result = false;
                 break;
@@ -862,6 +862,8 @@ check_expected_areq(AuthRequest areq, PGconn *conn)
 int
 pg_fe_sendauth(AuthRequest areq, int payloadlen, PGconn *conn)
 {
+    int            oldmsglen;
+
     if (!check_expected_areq(areq, conn))
         return STATUS_ERROR;

@@ -871,12 +873,12 @@ pg_fe_sendauth(AuthRequest areq, int payloadlen, PGconn *conn)
             break;

         case AUTH_REQ_KRB4:
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("Kerberos 4 authentication not supported\n"));
             return STATUS_ERROR;

         case AUTH_REQ_KRB5:
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("Kerberos 5 authentication not supported\n"));
             return STATUS_ERROR;

@@ -947,7 +949,7 @@ pg_fe_sendauth(AuthRequest areq, int payloadlen, PGconn *conn)
             /* No GSSAPI *or* SSPI support */
         case AUTH_REQ_GSS:
         case AUTH_REQ_GSS_CONT:
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("GSSAPI authentication not supported\n"));
             return STATUS_ERROR;
 #endif                            /* defined(ENABLE_GSS) || defined(ENABLE_SSPI) */
@@ -979,7 +981,7 @@ pg_fe_sendauth(AuthRequest areq, int payloadlen, PGconn *conn)
              */
 #if !defined(ENABLE_GSS)
         case AUTH_REQ_SSPI:
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("SSPI authentication not supported\n"));
             return STATUS_ERROR;
 #endif                            /* !define(ENABLE_GSS) */
@@ -987,7 +989,7 @@ pg_fe_sendauth(AuthRequest areq, int payloadlen, PGconn *conn)


         case AUTH_REQ_CRYPT:
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("Crypt authentication not supported\n"));
             return STATUS_ERROR;

@@ -1002,13 +1004,13 @@ pg_fe_sendauth(AuthRequest areq, int payloadlen, PGconn *conn)
                     password = conn->pgpass;
                 if (password == NULL || password[0] == '\0')
                 {
-                    printfPQExpBuffer(&conn->errorMessage,
+                    appendPQExpBuffer(&conn->errorMessage,
                                       PQnoPasswordSupplied);
                     return STATUS_ERROR;
                 }
                 if (pg_password_sendauth(conn, password, areq) != STATUS_OK)
                 {
-                    printfPQExpBuffer(&conn->errorMessage,
+                    appendPQExpBuffer(&conn->errorMessage,
                                       "fe_sendauth: error sending password authentication\n");
                     return STATUS_ERROR;
                 }
@@ -1032,16 +1034,17 @@ pg_fe_sendauth(AuthRequest areq, int payloadlen, PGconn *conn)
         case AUTH_REQ_SASL_FIN:
             if (conn->sasl_state == NULL)
             {
-                printfPQExpBuffer(&conn->errorMessage,
+                appendPQExpBuffer(&conn->errorMessage,
                                   "fe_sendauth: invalid authentication request from server: AUTH_REQ_SASL_CONT without
AUTH_REQ_SASL\n");
                 return STATUS_ERROR;
             }
+            oldmsglen = conn->errorMessage.len;
             if (pg_SASL_continue(conn, payloadlen,
                                  (areq == AUTH_REQ_SASL_FIN)) != STATUS_OK)
             {
-                /* Use error message, if set already */
-                if (conn->errorMessage.len == 0)
-                    printfPQExpBuffer(&conn->errorMessage,
+                /* Use this message if pg_SASL_continue didn't supply one */
+                if (conn->errorMessage.len == oldmsglen)
+                    appendPQExpBuffer(&conn->errorMessage,
                                       "fe_sendauth: error in SASL authentication\n");
                 return STATUS_ERROR;
             }
@@ -1053,7 +1056,7 @@ pg_fe_sendauth(AuthRequest areq, int payloadlen, PGconn *conn)
             break;

         default:
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("authentication method %u not supported\n"), areq);
             return STATUS_ERROR;
     }
@@ -1067,7 +1070,7 @@ pg_fe_sendauth(AuthRequest areq, int payloadlen, PGconn *conn)
  *
  * Returns a pointer to malloc'd space containing whatever name the user
  * has authenticated to the system.  If there is an error, return NULL,
- * and put a suitable error message in *errorMessage if that's not NULL.
+ * and append a suitable error message to *errorMessage if that's not NULL.
  */
 char *
 pg_fe_getauthname(PQExpBuffer errorMessage)
@@ -1100,7 +1103,7 @@ pg_fe_getauthname(PQExpBuffer errorMessage)
     if (GetUserName(username, &namesize))
         name = username;
     else if (errorMessage)
-        printfPQExpBuffer(errorMessage,
+        appendPQExpBuffer(errorMessage,
                           libpq_gettext("user name lookup failure: error code %lu\n"),
                           GetLastError());
 #else
@@ -1110,12 +1113,12 @@ pg_fe_getauthname(PQExpBuffer errorMessage)
     else if (errorMessage)
     {
         if (pwerr != 0)
-            printfPQExpBuffer(errorMessage,
+            appendPQExpBuffer(errorMessage,
                               libpq_gettext("could not look up local user ID %d: %s\n"),
                               (int) user_id,
                               strerror_r(pwerr, pwdbuf, sizeof(pwdbuf)));
         else
-            printfPQExpBuffer(errorMessage,
+            appendPQExpBuffer(errorMessage,
                               libpq_gettext("local user with ID %d does not exist\n"),
                               (int) user_id);
     }
@@ -1125,7 +1128,7 @@ pg_fe_getauthname(PQExpBuffer errorMessage)
     {
         result = strdup(name);
         if (result == NULL && errorMessage)
-            printfPQExpBuffer(errorMessage,
+            appendPQExpBuffer(errorMessage,
                               libpq_gettext("out of memory\n"));
     }

@@ -1196,6 +1199,8 @@ PQencryptPasswordConn(PGconn *conn, const char *passwd, const char *user,
     if (!conn)
         return NULL;

+    resetPQExpBuffer(&conn->errorMessage);
+
     /* If no algorithm was given, ask the server. */
     if (algorithm == NULL)
     {
@@ -1217,7 +1222,7 @@ PQencryptPasswordConn(PGconn *conn, const char *passwd, const char *user,
         if (PQntuples(res) != 1 || PQnfields(res) != 1)
         {
             PQclear(res);
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("unexpected shape of result set returned for SHOW\n"));
             return NULL;
         }
@@ -1226,7 +1231,7 @@ PQencryptPasswordConn(PGconn *conn, const char *passwd, const char *user,
         if (strlen(val) > MAX_ALGORITHM_NAME_LEN)
         {
             PQclear(res);
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("password_encryption value too long\n"));
             return NULL;
         }
@@ -1266,14 +1271,14 @@ PQencryptPasswordConn(PGconn *conn, const char *passwd, const char *user,
     }
     else
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("unrecognized password encryption algorithm \"%s\"\n"),
                           algorithm);
         return NULL;
     }

     if (!crypt_pwd)
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("out of memory\n"));

     return crypt_pwd;
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index a834ce8cf0..84e2868104 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -755,7 +755,9 @@ PQconnectStartParams(const char *const *keywords,
     PQconninfoOption *connOptions;

     /*
-     * Allocate memory for the conn structure
+     * Allocate memory for the conn structure.  Note that we also expect this
+     * to initialize conn->errorMessage to empty.  All subsequent steps during
+     * connection initialization will only append to that buffer.
      */
     conn = makeEmptyPGconn();
     if (conn == NULL)
@@ -831,7 +833,9 @@ PQconnectStart(const char *conninfo)
     PGconn       *conn;

     /*
-     * Allocate memory for the conn structure
+     * Allocate memory for the conn structure.  Note that we also expect this
+     * to initialize conn->errorMessage to empty.  All subsequent steps during
+     * connection initialization will only append to that buffer.
      */
     conn = makeEmptyPGconn();
     if (conn == NULL)
@@ -889,7 +893,7 @@ fillPGconn(PGconn *conn, PQconninfoOption *connOptions)
                 *connmember = strdup(tmp);
                 if (*connmember == NULL)
                 {
-                    printfPQExpBuffer(&conn->errorMessage,
+                    appendPQExpBuffer(&conn->errorMessage,
                                       libpq_gettext("out of memory\n"));
                     return false;
                 }
@@ -1072,7 +1076,7 @@ connectOptions2(PGconn *conn)
         if (more || i != conn->nconnhost)
         {
             conn->status = CONNECTION_BAD;
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("could not match %d host names to %d hostaddr values\n"),
                               count_comma_separated_elems(conn->pghost), conn->nconnhost);
             return false;
@@ -1153,7 +1157,7 @@ connectOptions2(PGconn *conn)
         else if (more || i != conn->nconnhost)
         {
             conn->status = CONNECTION_BAD;
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("could not match %d port numbers to %d hosts\n"),
                               count_comma_separated_elems(conn->pgport), conn->nconnhost);
             return false;
@@ -1246,7 +1250,7 @@ connectOptions2(PGconn *conn)
             && strcmp(conn->channel_binding, "require") != 0)
         {
             conn->status = CONNECTION_BAD;
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("invalid %s value: \"%s\"\n"),
                               "channel_binding", conn->channel_binding);
             return false;
@@ -1272,7 +1276,7 @@ connectOptions2(PGconn *conn)
             && strcmp(conn->sslmode, "verify-full") != 0)
         {
             conn->status = CONNECTION_BAD;
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("invalid %s value: \"%s\"\n"),
                               "sslmode", conn->sslmode);
             return false;
@@ -1293,7 +1297,7 @@ connectOptions2(PGconn *conn)
             case 'r':            /* "require" */
             case 'v':            /* "verify-ca" or "verify-full" */
                 conn->status = CONNECTION_BAD;
-                printfPQExpBuffer(&conn->errorMessage,
+                appendPQExpBuffer(&conn->errorMessage,
                                   libpq_gettext("sslmode value \"%s\" invalid when SSL support is not compiled in\n"),
                                   conn->sslmode);
                 return false;
@@ -1314,7 +1318,7 @@ connectOptions2(PGconn *conn)
     if (!sslVerifyProtocolVersion(conn->ssl_min_protocol_version))
     {
         conn->status = CONNECTION_BAD;
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("invalid %s value: \"%s\"\n"),
                           "ssl_min_protocol_version",
                           conn->ssl_min_protocol_version);
@@ -1323,7 +1327,7 @@ connectOptions2(PGconn *conn)
     if (!sslVerifyProtocolVersion(conn->ssl_max_protocol_version))
     {
         conn->status = CONNECTION_BAD;
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("invalid %s value: \"%s\"\n"),
                           "ssl_max_protocol_version",
                           conn->ssl_max_protocol_version);
@@ -1341,7 +1345,7 @@ connectOptions2(PGconn *conn)
                                 conn->ssl_max_protocol_version))
     {
         conn->status = CONNECTION_BAD;
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("invalid SSL protocol version range\n"));
         return false;
     }
@@ -1356,7 +1360,7 @@ connectOptions2(PGconn *conn)
             strcmp(conn->gssencmode, "require") != 0)
         {
             conn->status = CONNECTION_BAD;
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("invalid %s value: \"%s\"\n"),
                               "gssencmode",
                               conn->gssencmode);
@@ -1366,7 +1370,7 @@ connectOptions2(PGconn *conn)
         if (strcmp(conn->gssencmode, "require") == 0)
         {
             conn->status = CONNECTION_BAD;
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("gssencmode value \"%s\" invalid when GSSAPI support is not compiled
in\n"),
                               conn->gssencmode);
             return false;
@@ -1401,7 +1405,7 @@ connectOptions2(PGconn *conn)
             && strcmp(conn->target_session_attrs, "read-write") != 0)
         {
             conn->status = CONNECTION_BAD;
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("invalid %s value: \"%s\"\n"),
                               "target_settion_attrs",
                               conn->target_session_attrs);
@@ -1420,7 +1424,7 @@ connectOptions2(PGconn *conn)

 oom_error:
     conn->status = CONNECTION_BAD;
-    printfPQExpBuffer(&conn->errorMessage,
+    appendPQExpBuffer(&conn->errorMessage,
                       libpq_gettext("out of memory\n"));
     return false;
 }
@@ -1487,7 +1491,9 @@ PQsetdbLogin(const char *pghost, const char *pgport, const char *pgoptions,
     PGconn       *conn;

     /*
-     * Allocate memory for the conn structure
+     * Allocate memory for the conn structure.  Note that we also expect this
+     * to initialize conn->errorMessage to empty.  All subsequent steps during
+     * connection initialization will only append to that buffer.
      */
     conn = makeEmptyPGconn();
     if (conn == NULL)
@@ -1596,7 +1602,7 @@ PQsetdbLogin(const char *pghost, const char *pgport, const char *pgoptions,

 oom_error:
     conn->status = CONNECTION_BAD;
-    printfPQExpBuffer(&conn->errorMessage,
+    appendPQExpBuffer(&conn->errorMessage,
                       libpq_gettext("out of memory\n"));
     return conn;
 }
@@ -2017,7 +2023,7 @@ connectDBStart(PGconn *conn)
      */
     if (!pg_link_canary_is_frontend())
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           "libpq is incorrectly linked to backend functions\n");
         goto connect_errReturn;
     }
@@ -2026,14 +2032,6 @@ connectDBStart(PGconn *conn)
     conn->inStart = conn->inCursor = conn->inEnd = 0;
     conn->outCount = 0;

-    /*
-     * Ensure errorMessage is empty, too.  PQconnectPoll will append messages
-     * to it in the process of scanning for a working server.  Thus, if we
-     * fail to connect to multiple hosts, the final error message will include
-     * details about each failure.
-     */
-    resetPQExpBuffer(&conn->errorMessage);
-
     /*
      * Set up to try to connect to the first host.  (Setting whichhost = -1 is
      * a bit of a cheat, but PQconnectPoll will advance it to 0 before
@@ -2139,12 +2137,6 @@ connectDBComplete(PGconn *conn)
         switch (flag)
         {
             case PGRES_POLLING_OK:
-
-                /*
-                 * Reset stored error messages since we now have a working
-                 * connection
-                 */
-                resetPQExpBuffer(&conn->errorMessage);
                 return 1;        /* success! */

             case PGRES_POLLING_READING:
@@ -2189,46 +2181,6 @@ connectDBComplete(PGconn *conn)
     }
 }

-/*
- * This subroutine saves conn->errorMessage, which will be restored back by
- * restoreErrorMessage subroutine.  Returns false on OOM failure.
- */
-static bool
-saveErrorMessage(PGconn *conn, PQExpBuffer savedMessage)
-{
-    initPQExpBuffer(savedMessage);
-    appendPQExpBufferStr(savedMessage,
-                         conn->errorMessage.data);
-    if (PQExpBufferBroken(savedMessage))
-    {
-        printfPQExpBuffer(&conn->errorMessage,
-                          libpq_gettext("out of memory\n"));
-        return false;
-    }
-    /* Clear whatever is in errorMessage now */
-    resetPQExpBuffer(&conn->errorMessage);
-    return true;
-}
-
-/*
- * Restores saved error messages back to conn->errorMessage, prepending them
- * to whatever is in conn->errorMessage already.  (This does the right thing
- * if anything's been added to conn->errorMessage since saveErrorMessage.)
- */
-static void
-restoreErrorMessage(PGconn *conn, PQExpBuffer savedMessage)
-{
-    appendPQExpBufferStr(savedMessage, conn->errorMessage.data);
-    resetPQExpBuffer(&conn->errorMessage);
-    appendPQExpBufferStr(&conn->errorMessage, savedMessage->data);
-    /* If any step above hit OOM, just report that */
-    if (PQExpBufferBroken(savedMessage) ||
-        PQExpBufferBroken(&conn->errorMessage))
-        printfPQExpBuffer(&conn->errorMessage,
-                          libpq_gettext("out of memory\n"));
-    termPQExpBuffer(savedMessage);
-}
-
 /* ----------------
  *        PQconnectPoll
  *
@@ -2264,7 +2216,6 @@ PQconnectPoll(PGconn *conn)
     PGresult   *res;
     char        sebuf[PG_STRERROR_R_BUFLEN];
     int            optval;
-    PQExpBufferData savedMessage;

     if (conn == NULL)
         return PGRES_POLLING_FAILED;
@@ -2954,11 +2905,7 @@ keep_going:                        /* We will come back to here until there is
                                                         EnvironmentOptions);
                 if (!startpacket)
                 {
-                    /*
-                     * will not appendbuffer here, since it's likely to also
-                     * run out of memory
-                     */
-                    printfPQExpBuffer(&conn->errorMessage,
+                    appendPQExpBuffer(&conn->errorMessage,
                                       libpq_gettext("out of memory\n"));
                     goto error_return;
                 }
@@ -3448,7 +3395,6 @@ keep_going:                        /* We will come back to here until there is
                  * avoid the Kerberos code doing a hostname look-up.
                  */
                 res = pg_fe_sendauth(areq, msgLength, conn);
-                conn->errorMessage.len = strlen(conn->errorMessage.data);

                 /* OK, we have processed the message; mark data consumed */
                 conn->inStart = conn->inCursor;
@@ -3576,24 +3522,16 @@ keep_going:                        /* We will come back to here until there is
                     strcmp(conn->target_session_attrs, "read-write") == 0)
                 {
                     /*
-                     * Save existing error messages across the PQsendQuery
-                     * attempt.  This is necessary because PQsendQuery is
-                     * going to reset conn->errorMessage, so we would lose
-                     * error messages related to previous hosts we have tried
-                     * and failed to connect to.
+                     * We use PQsendQueryContinue so that conn->errorMessage
+                     * does not get cleared.  We need to preserve any error
+                     * messages related to previous hosts we have tried and
+                     * failed to connect to.
                      */
-                    if (!saveErrorMessage(conn, &savedMessage))
-                        goto error_return;
-
                     conn->status = CONNECTION_OK;
-                    if (!PQsendQuery(conn,
-                                     "SHOW transaction_read_only"))
-                    {
-                        restoreErrorMessage(conn, &savedMessage);
+                    if (!PQsendQueryContinue(conn,
+                                             "SHOW transaction_read_only"))
                         goto error_return;
-                    }
                     conn->status = CONNECTION_CHECK_WRITABLE;
-                    restoreErrorMessage(conn, &savedMessage);
                     return PGRES_POLLING_READING;
                 }

@@ -3673,20 +3611,13 @@ keep_going:                        /* We will come back to here until there is
                 const char *displayed_host;
                 const char *displayed_port;

-                if (!saveErrorMessage(conn, &savedMessage))
-                    goto error_return;
-
                 conn->status = CONNECTION_OK;
                 if (!PQconsumeInput(conn))
-                {
-                    restoreErrorMessage(conn, &savedMessage);
                     goto error_return;
-                }

                 if (PQisBusy(conn))
                 {
                     conn->status = CONNECTION_CHECK_WRITABLE;
-                    restoreErrorMessage(conn, &savedMessage);
                     return PGRES_POLLING_READING;
                 }

@@ -3701,7 +3632,6 @@ keep_going:                        /* We will come back to here until there is
                     {
                         /* Not writable; fail this connection. */
                         PQclear(res);
-                        restoreErrorMessage(conn, &savedMessage);

                         /* Append error report to conn->errorMessage. */
                         if (conn->connhost[conn->whichhost].type == CHT_HOST_ADDRESS)
@@ -3732,7 +3662,6 @@ keep_going:                        /* We will come back to here until there is

                     /* Session is read-write, so we're good. */
                     PQclear(res);
-                    termPQExpBuffer(&savedMessage);

                     /*
                      * Finish reading any remaining messages before being
@@ -3748,7 +3677,6 @@ keep_going:                        /* We will come back to here until there is
                  */
                 if (res)
                     PQclear(res);
-                restoreErrorMessage(conn, &savedMessage);

                 /* Append error report to conn->errorMessage. */
                 if (conn->connhost[conn->whichhost].type == CHT_HOST_ADDRESS)
@@ -4157,6 +4085,9 @@ closePGconn(PGconn *conn)

     /*
      * Close the connection, reset all transient state, flush I/O buffers.
+     * Note that this includes clearing conn->errorMessage; we're no longer
+     * interested in any failures associated with the old connection, and we
+     * want a clean slate for any new connection attempt.
      */
     pqDropConnection(conn, true);
     conn->status = CONNECTION_BAD;    /* Well, not really _bad_ - just absent */
@@ -4212,7 +4143,7 @@ PQreset(PGconn *conn)
                                           conn->events[i].passThrough))
                 {
                     conn->status = CONNECTION_BAD;
-                    printfPQExpBuffer(&conn->errorMessage,
+                    appendPQExpBuffer(&conn->errorMessage,
                                       libpq_gettext("PGEventProc \"%s\" failed during PGEVT_CONNRESET event\n"),
                                       conn->events[i].name);
                     break;
@@ -4272,7 +4203,7 @@ PQresetPoll(PGconn *conn)
                                           conn->events[i].passThrough))
                 {
                     conn->status = CONNECTION_BAD;
-                    printfPQExpBuffer(&conn->errorMessage,
+                    appendPQExpBuffer(&conn->errorMessage,
                                       libpq_gettext("PGEventProc \"%s\" failed during PGEVT_CONNRESET event\n"),
                                       conn->events[i].name);
                     return PGRES_POLLING_FAILED;
@@ -4569,7 +4500,7 @@ pqPacketSend(PGconn *conn, char pack_type,
  *    2 if a connection could not be established, and
  *    3 if a fatal error occurred.
  *
- * An error message is returned in the third argument for return codes 1 and 3.
+ * An error message is appended to *errorMessage for return codes 1 and 3.
  */
 static int
 ldapServiceLookup(const char *purl, PQconninfoOption *options,
@@ -4607,7 +4538,7 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options,

     if ((url = strdup(purl)) == NULL)
     {
-        printfPQExpBuffer(errorMessage, libpq_gettext("out of memory\n"));
+        appendPQExpBuffer(errorMessage, libpq_gettext("out of memory\n"));
         return 3;
     }

@@ -4619,7 +4550,7 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options,

     if (pg_strncasecmp(url, LDAP_URL, strlen(LDAP_URL)) != 0)
     {
-        printfPQExpBuffer(errorMessage,
+        appendPQExpBuffer(errorMessage,
                           libpq_gettext("invalid LDAP URL \"%s\": scheme must be ldap://\n"), purl);
         free(url);
         return 3;
@@ -4634,7 +4565,7 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options,
     p = strchr(url + strlen(LDAP_URL), '/');
     if (p == NULL || *(p + 1) == '\0' || *(p + 1) == '?')
     {
-        printfPQExpBuffer(errorMessage,
+        appendPQExpBuffer(errorMessage,
                           libpq_gettext("invalid LDAP URL \"%s\": missing distinguished name\n"),
                           purl);
         free(url);
@@ -4646,7 +4577,7 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options,
     /* attribute */
     if ((p = strchr(dn, '?')) == NULL || *(p + 1) == '\0' || *(p + 1) == '?')
     {
-        printfPQExpBuffer(errorMessage,
+        appendPQExpBuffer(errorMessage,
                           libpq_gettext("invalid LDAP URL \"%s\": must have exactly one attribute\n"),
                           purl);
         free(url);
@@ -4658,7 +4589,7 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options,
     /* scope */
     if ((p = strchr(attrs[0], '?')) == NULL || *(p + 1) == '\0' || *(p + 1) == '?')
     {
-        printfPQExpBuffer(errorMessage, libpq_gettext("invalid LDAP URL \"%s\": must have search scope
(base/one/sub)\n"),purl); 
+        appendPQExpBuffer(errorMessage, libpq_gettext("invalid LDAP URL \"%s\": must have search scope
(base/one/sub)\n"),purl); 
         free(url);
         return 3;
     }
@@ -4668,7 +4599,7 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options,
     /* filter */
     if ((p = strchr(scopestr, '?')) == NULL || *(p + 1) == '\0' || *(p + 1) == '?')
     {
-        printfPQExpBuffer(errorMessage,
+        appendPQExpBuffer(errorMessage,
                           libpq_gettext("invalid LDAP URL \"%s\": no filter\n"), purl);
         free(url);
         return 3;
@@ -4689,7 +4620,7 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options,
         lport = strtol(portstr, &endptr, 10);
         if (*portstr == '\0' || *endptr != '\0' || errno || lport < 0 || lport > 65535)
         {
-            printfPQExpBuffer(errorMessage,
+            appendPQExpBuffer(errorMessage,
                               libpq_gettext("invalid LDAP URL \"%s\": invalid port number\n"),
                               purl);
             free(url);
@@ -4701,7 +4632,7 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options,
     /* Allow only one attribute */
     if (strchr(attrs[0], ',') != NULL)
     {
-        printfPQExpBuffer(errorMessage,
+        appendPQExpBuffer(errorMessage,
                           libpq_gettext("invalid LDAP URL \"%s\": must have exactly one attribute\n"),
                           purl);
         free(url);
@@ -4717,7 +4648,7 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options,
         scope = LDAP_SCOPE_SUBTREE;
     else
     {
-        printfPQExpBuffer(errorMessage, libpq_gettext("invalid LDAP URL \"%s\": must have search scope
(base/one/sub)\n"),purl); 
+        appendPQExpBuffer(errorMessage, libpq_gettext("invalid LDAP URL \"%s\": must have search scope
(base/one/sub)\n"),purl); 
         free(url);
         return 3;
     }
@@ -4725,7 +4656,7 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options,
     /* initialize LDAP structure */
     if ((ld = ldap_init(hostname, port)) == NULL)
     {
-        printfPQExpBuffer(errorMessage,
+        appendPQExpBuffer(errorMessage,
                           libpq_gettext("could not create LDAP structure\n"));
         free(url);
         return 3;
@@ -4801,7 +4732,7 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options,
     {
         if (res != NULL)
             ldap_msgfree(res);
-        printfPQExpBuffer(errorMessage,
+        appendPQExpBuffer(errorMessage,
                           libpq_gettext("lookup on LDAP server failed: %s\n"),
                           ldap_err2string(rc));
         ldap_unbind(ld);
@@ -4812,7 +4743,7 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options,
     /* complain if there was not exactly one result */
     if ((rc = ldap_count_entries(ld, res)) != 1)
     {
-        printfPQExpBuffer(errorMessage,
+        appendPQExpBuffer(errorMessage,
                           rc ? libpq_gettext("more than one entry found on LDAP lookup\n")
                           : libpq_gettext("no entry found on LDAP lookup\n"));
         ldap_msgfree(res);
@@ -4825,7 +4756,7 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options,
     if ((entry = ldap_first_entry(ld, res)) == NULL)
     {
         /* should never happen */
-        printfPQExpBuffer(errorMessage,
+        appendPQExpBuffer(errorMessage,
                           libpq_gettext("no entry found on LDAP lookup\n"));
         ldap_msgfree(res);
         ldap_unbind(ld);
@@ -4836,7 +4767,7 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options,
     /* get values */
     if ((values = ldap_get_values_len(ld, entry, attrs[0])) == NULL)
     {
-        printfPQExpBuffer(errorMessage,
+        appendPQExpBuffer(errorMessage,
                           libpq_gettext("attribute has no values on LDAP lookup\n"));
         ldap_msgfree(res);
         ldap_unbind(ld);
@@ -4849,7 +4780,7 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options,

     if (values[0] == NULL)
     {
-        printfPQExpBuffer(errorMessage,
+        appendPQExpBuffer(errorMessage,
                           libpq_gettext("attribute has no values on LDAP lookup\n"));
         ldap_value_free_len(values);
         ldap_unbind(ld);
@@ -4862,7 +4793,7 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options,
         size += values[i]->bv_len + 1;
     if ((result = malloc(size)) == NULL)
     {
-        printfPQExpBuffer(errorMessage,
+        appendPQExpBuffer(errorMessage,
                           libpq_gettext("out of memory\n"));
         ldap_value_free_len(values);
         ldap_unbind(ld);
@@ -4901,7 +4832,7 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options,
                 }
                 else if (ld_is_nl_cr(*p))
                 {
-                    printfPQExpBuffer(errorMessage,
+                    appendPQExpBuffer(errorMessage,
                                       libpq_gettext("missing \"=\" after \"%s\" in connection info string\n"),
                                       optname);
                     free(result);
@@ -4920,7 +4851,7 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options,
                 }
                 else if (!ld_is_sp_tab(*p))
                 {
-                    printfPQExpBuffer(errorMessage,
+                    appendPQExpBuffer(errorMessage,
                                       libpq_gettext("missing \"=\" after \"%s\" in connection info string\n"),
                                       optname);
                     free(result);
@@ -4981,7 +4912,7 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options,
                         options[i].val = strdup(optval);
                         if (!options[i].val)
                         {
-                            printfPQExpBuffer(errorMessage,
+                            appendPQExpBuffer(errorMessage,
                                               libpq_gettext("out of memory\n"));
                             free(result);
                             return 3;
@@ -4993,7 +4924,7 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options,
             }
             if (!found_keyword)
             {
-                printfPQExpBuffer(errorMessage,
+                appendPQExpBuffer(errorMessage,
                                   libpq_gettext("invalid connection option \"%s\"\n"),
                                   optname);
                 free(result);
@@ -5009,7 +4940,7 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options,

     if (state == 5 || state == 6)
     {
-        printfPQExpBuffer(errorMessage,
+        appendPQExpBuffer(errorMessage,
                           libpq_gettext("unterminated quoted string in connection info string\n"));
         return 3;
     }
@@ -5090,7 +5021,7 @@ next_file:
 last_file:
     if (!group_found)
     {
-        printfPQExpBuffer(errorMessage,
+        appendPQExpBuffer(errorMessage,
                           libpq_gettext("definition of service \"%s\" not found\n"), service);
         return 3;
     }
@@ -5117,7 +5048,7 @@ parseServiceFile(const char *serviceFile,
     f = fopen(serviceFile, "r");
     if (f == NULL)
     {
-        printfPQExpBuffer(errorMessage, libpq_gettext("service file \"%s\" not found\n"),
+        appendPQExpBuffer(errorMessage, libpq_gettext("service file \"%s\" not found\n"),
                           serviceFile);
         return 1;
     }
@@ -5193,7 +5124,7 @@ parseServiceFile(const char *serviceFile,
                 val = strchr(line, '=');
                 if (val == NULL)
                 {
-                    printfPQExpBuffer(errorMessage,
+                    appendPQExpBuffer(errorMessage,
                                       libpq_gettext("syntax error in service file \"%s\", line %d\n"),
                                       serviceFile,
                                       linenr);
@@ -5204,7 +5135,7 @@ parseServiceFile(const char *serviceFile,

                 if (strcmp(key, "service") == 0)
                 {
-                    printfPQExpBuffer(errorMessage,
+                    appendPQExpBuffer(errorMessage,
                                       libpq_gettext("nested service specifications not supported in service file
\"%s\",line %d\n"), 
                                       serviceFile,
                                       linenr);
@@ -5225,7 +5156,7 @@ parseServiceFile(const char *serviceFile,
                             options[i].val = strdup(val);
                         if (!options[i].val)
                         {
-                            printfPQExpBuffer(errorMessage,
+                            appendPQExpBuffer(errorMessage,
                                               libpq_gettext("out of memory\n"));
                             result = 3;
                             goto exit;
@@ -5237,7 +5168,7 @@ parseServiceFile(const char *serviceFile,

                 if (!found_keyword)
                 {
-                    printfPQExpBuffer(errorMessage,
+                    appendPQExpBuffer(errorMessage,
                                       libpq_gettext("syntax error in service file \"%s\", line %d\n"),
                                       serviceFile,
                                       linenr);
@@ -5307,7 +5238,7 @@ conninfo_init(PQExpBuffer errorMessage)
     options = (PQconninfoOption *) malloc(sizeof(PQconninfoOption) * sizeof(PQconninfoOptions) /
sizeof(PQconninfoOptions[0]));
     if (options == NULL)
     {
-        printfPQExpBuffer(errorMessage,
+        appendPQExpBuffer(errorMessage,
                           libpq_gettext("out of memory\n"));
         return NULL;
     }
@@ -5328,7 +5259,7 @@ conninfo_init(PQExpBuffer errorMessage)
  * Connection string parser
  *
  * Returns a malloc'd PQconninfoOption array, if parsing is successful.
- * Otherwise, NULL is returned and an error message is left in errorMessage.
+ * Otherwise, NULL is returned and an error message is added to errorMessage.
  *
  * If use_defaults is true, default values are filled in (from a service file,
  * environment variables, etc).
@@ -5406,7 +5337,7 @@ conninfo_parse(const char *conninfo, PQExpBuffer errorMessage,
     /* Need a modifiable copy of the input string */
     if ((buf = strdup(conninfo)) == NULL)
     {
-        printfPQExpBuffer(errorMessage,
+        appendPQExpBuffer(errorMessage,
                           libpq_gettext("out of memory\n"));
         PQconninfoFree(options);
         return NULL;
@@ -5445,7 +5376,7 @@ conninfo_parse(const char *conninfo, PQExpBuffer errorMessage,
         /* Check that there is a following '=' */
         if (*cp != '=')
         {
-            printfPQExpBuffer(errorMessage,
+            appendPQExpBuffer(errorMessage,
                               libpq_gettext("missing \"=\" after \"%s\" in connection info string\n"),
                               pname);
             PQconninfoFree(options);
@@ -5494,7 +5425,7 @@ conninfo_parse(const char *conninfo, PQExpBuffer errorMessage,
             {
                 if (*cp == '\0')
                 {
-                    printfPQExpBuffer(errorMessage,
+                    appendPQExpBuffer(errorMessage,
                                       libpq_gettext("unterminated quoted string in connection info string\n"));
                     PQconninfoFree(options);
                     free(buf);
@@ -5551,7 +5482,7 @@ conninfo_parse(const char *conninfo, PQExpBuffer errorMessage,
  *
  * If successful, a malloc'd PQconninfoOption array is returned.
  * If not successful, NULL is returned and an error message is
- * left in errorMessage.
+ * appended to errorMessage.
  * Defaults are supplied (from a service file, environment variables, etc)
  * for unspecified options, but only if use_defaults is true.
  *
@@ -5630,7 +5561,7 @@ conninfo_array_parse(const char *const *keywords, const char *const *values,
             /* Check for invalid connection option */
             if (option->keyword == NULL)
             {
-                printfPQExpBuffer(errorMessage,
+                appendPQExpBuffer(errorMessage,
                                   libpq_gettext("invalid connection option \"%s\"\n"),
                                   pname);
                 PQconninfoFree(options);
@@ -5662,7 +5593,7 @@ conninfo_array_parse(const char *const *keywords, const char *const *values,
                                 options[k].val = strdup(str_option->val);
                                 if (!options[k].val)
                                 {
-                                    printfPQExpBuffer(errorMessage,
+                                    appendPQExpBuffer(errorMessage,
                                                       libpq_gettext("out of memory\n"));
                                     PQconninfoFree(options);
                                     PQconninfoFree(dbname_options);
@@ -5691,7 +5622,7 @@ conninfo_array_parse(const char *const *keywords, const char *const *values,
                 option->val = strdup(pvalue);
                 if (!option->val)
                 {
-                    printfPQExpBuffer(errorMessage,
+                    appendPQExpBuffer(errorMessage,
                                       libpq_gettext("out of memory\n"));
                     PQconninfoFree(options);
                     PQconninfoFree(dbname_options);
@@ -5763,7 +5694,7 @@ conninfo_add_defaults(PQconninfoOption *options, PQExpBuffer errorMessage)
                 if (!option->val)
                 {
                     if (errorMessage)
-                        printfPQExpBuffer(errorMessage,
+                        appendPQExpBuffer(errorMessage,
                                           libpq_gettext("out of memory\n"));
                     return false;
                 }
@@ -5787,7 +5718,7 @@ conninfo_add_defaults(PQconninfoOption *options, PQExpBuffer errorMessage)
                 if (!option->val)
                 {
                     if (errorMessage)
-                        printfPQExpBuffer(errorMessage,
+                        appendPQExpBuffer(errorMessage,
                                           libpq_gettext("out of memory\n"));
                     return false;
                 }
@@ -5805,7 +5736,7 @@ conninfo_add_defaults(PQconninfoOption *options, PQExpBuffer errorMessage)
             if (!option->val)
             {
                 if (errorMessage)
-                    printfPQExpBuffer(errorMessage,
+                    appendPQExpBuffer(errorMessage,
                                       libpq_gettext("out of memory\n"));
                 return false;
             }
@@ -5906,7 +5837,7 @@ conninfo_uri_parse_options(PQconninfoOption *options, const char *uri,
     initPQExpBuffer(&portbuf);
     if (PQExpBufferDataBroken(hostbuf) || PQExpBufferDataBroken(portbuf))
     {
-        printfPQExpBuffer(errorMessage,
+        appendPQExpBuffer(errorMessage,
                           libpq_gettext("out of memory\n"));
         goto cleanup;
     }
@@ -5915,7 +5846,7 @@ conninfo_uri_parse_options(PQconninfoOption *options, const char *uri,
     buf = strdup(uri);
     if (buf == NULL)
     {
-        printfPQExpBuffer(errorMessage,
+        appendPQExpBuffer(errorMessage,
                           libpq_gettext("out of memory\n"));
         goto cleanup;
     }
@@ -5926,7 +5857,7 @@ conninfo_uri_parse_options(PQconninfoOption *options, const char *uri,
     if (prefix_len == 0)
     {
         /* Should never happen */
-        printfPQExpBuffer(errorMessage,
+        appendPQExpBuffer(errorMessage,
                           libpq_gettext("invalid URI propagated to internal parser routine: \"%s\"\n"),
                           uri);
         goto cleanup;
@@ -6003,14 +5934,14 @@ conninfo_uri_parse_options(PQconninfoOption *options, const char *uri,
                 ++p;
             if (!*p)
             {
-                printfPQExpBuffer(errorMessage,
+                appendPQExpBuffer(errorMessage,
                                   libpq_gettext("end of string reached when looking for matching \"]\" in IPv6 host
addressin URI: \"%s\"\n"), 
                                   uri);
                 goto cleanup;
             }
             if (p == host)
             {
-                printfPQExpBuffer(errorMessage,
+                appendPQExpBuffer(errorMessage,
                                   libpq_gettext("IPv6 host address may not be empty in URI: \"%s\"\n"),
                                   uri);
                 goto cleanup;
@@ -6025,7 +5956,7 @@ conninfo_uri_parse_options(PQconninfoOption *options, const char *uri,
              */
             if (*p && *p != ':' && *p != '/' && *p != '?' && *p != ',')
             {
-                printfPQExpBuffer(errorMessage,
+                appendPQExpBuffer(errorMessage,
                                   libpq_gettext("unexpected character \"%c\" at position %d in URI (expected \":\" or
\"/\"):\"%s\"\n"), 
                                   *p, (int) (p - buf + 1), uri);
                 goto cleanup;
@@ -6142,6 +6073,7 @@ conninfo_uri_parse_params(char *params,
         char       *value = NULL;
         char       *p = params;
         bool        malloced = false;
+        int            oldmsglen;

         /*
          * Scan the params string for '=' and '&', marking the end of keyword
@@ -6154,7 +6086,7 @@ conninfo_uri_parse_params(char *params,
                 /* Was there '=' already? */
                 if (value != NULL)
                 {
-                    printfPQExpBuffer(errorMessage,
+                    appendPQExpBuffer(errorMessage,
                                       libpq_gettext("extra key/value separator \"=\" in URI query parameter:
\"%s\"\n"),
                                       keyword);
                     return false;
@@ -6174,7 +6106,7 @@ conninfo_uri_parse_params(char *params,
                 /* Was there '=' at all? */
                 if (value == NULL)
                 {
-                    printfPQExpBuffer(errorMessage,
+                    appendPQExpBuffer(errorMessage,
                                       libpq_gettext("missing key/value separator \"=\" in URI query parameter:
\"%s\"\n"),
                                       keyword);
                     return false;
@@ -6220,12 +6152,13 @@ conninfo_uri_parse_params(char *params,
          * otherwise.  At this point both keyword and value are not
          * URI-encoded.
          */
+        oldmsglen = errorMessage->len;
         if (!conninfo_storeval(connOptions, keyword, value,
                                errorMessage, true, false))
         {
             /* Insert generic message if conninfo_storeval didn't give one. */
-            if (errorMessage->len == 0)
-                printfPQExpBuffer(errorMessage,
+            if (errorMessage->len == oldmsglen)
+                appendPQExpBuffer(errorMessage,
                                   libpq_gettext("invalid URI query parameter: \"%s\"\n"),
                                   keyword);
             /* And fail. */
@@ -6272,7 +6205,7 @@ conninfo_uri_decode(const char *str, PQExpBuffer errorMessage)
     buf = malloc(strlen(str) + 1);
     if (buf == NULL)
     {
-        printfPQExpBuffer(errorMessage, libpq_gettext("out of memory\n"));
+        appendPQExpBuffer(errorMessage, libpq_gettext("out of memory\n"));
         return NULL;
     }
     p = buf;
@@ -6299,7 +6232,7 @@ conninfo_uri_decode(const char *str, PQExpBuffer errorMessage)
              */
             if (!(get_hexdigit(*q++, &hi) && get_hexdigit(*q++, &lo)))
             {
-                printfPQExpBuffer(errorMessage,
+                appendPQExpBuffer(errorMessage,
                                   libpq_gettext("invalid percent-encoded token: \"%s\"\n"),
                                   str);
                 free(buf);
@@ -6309,7 +6242,7 @@ conninfo_uri_decode(const char *str, PQExpBuffer errorMessage)
             c = (hi << 4) | lo;
             if (c == 0)
             {
-                printfPQExpBuffer(errorMessage,
+                appendPQExpBuffer(errorMessage,
                                   libpq_gettext("forbidden value %%00 in percent-encoded value: \"%s\"\n"),
                                   str);
                 free(buf);
@@ -6404,7 +6337,7 @@ conninfo_storeval(PQconninfoOption *connOptions,
     if (option == NULL)
     {
         if (!ignoreMissing)
-            printfPQExpBuffer(errorMessage,
+            appendPQExpBuffer(errorMessage,
                               libpq_gettext("invalid connection option \"%s\"\n"),
                               keyword);
         return NULL;
@@ -6422,7 +6355,7 @@ conninfo_storeval(PQconninfoOption *connOptions,
         value_copy = strdup(value);
         if (value_copy == NULL)
         {
-            printfPQExpBuffer(errorMessage, libpq_gettext("out of memory\n"));
+            appendPQExpBuffer(errorMessage, libpq_gettext("out of memory\n"));
             return NULL;
         }
     }
@@ -6469,7 +6402,10 @@ PQconninfo(PGconn *conn)
     if (conn == NULL)
         return NULL;

-    /* We don't actually report any errors here, but callees want a buffer */
+    /*
+     * We don't actually report any errors here, but callees want a buffer,
+     * and we prefer not to trash the conn's errorMessage.
+     */
     initPQExpBuffer(&errorBuf);
     if (PQExpBufferDataBroken(errorBuf))
         return NULL;            /* out of memory already :-( */
diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c
index d48f0fd587..39f7fefb3a 100644
--- a/src/interfaces/libpq/fe-exec.c
+++ b/src/interfaces/libpq/fe-exec.c
@@ -53,7 +53,8 @@ static bool static_std_strings = false;
 static PGEvent *dupEvents(PGEvent *events, int count, size_t *memSize);
 static bool pqAddTuple(PGresult *res, PGresAttValue *tup,
                        const char **errmsgp);
-static bool PQsendQueryStart(PGconn *conn);
+static int    PQsendQueryInternal(PGconn *conn, const char *query, bool newQuery);
+static bool PQsendQueryStart(PGconn *conn, bool newQuery);
 static int    PQsendQueryGuts(PGconn *conn,
                             const char *command,
                             const char *stmtName,
@@ -667,25 +668,6 @@ pqSetResultError(PGresult *res, const char *msg)
         res->errMsg = NULL;
 }

-/*
- * pqCatenateResultError -
- *        concatenate a new error message to the one already in a PGresult
- */
-void
-pqCatenateResultError(PGresult *res, const char *msg)
-{
-    PQExpBufferData errorBuf;
-
-    if (!res || !msg)
-        return;
-    initPQExpBuffer(&errorBuf);
-    if (res->errMsg)
-        appendPQExpBufferStr(&errorBuf, res->errMsg);
-    appendPQExpBufferStr(&errorBuf, msg);
-    pqSetResultError(res, errorBuf.data);
-    termPQExpBuffer(&errorBuf);
-}
-
 /*
  * PQclear -
  *      free's the memory associated with a PGresult
@@ -759,68 +741,46 @@ pqClearAsyncResult(PGconn *conn)
 /*
  * This subroutine deletes any existing async result, sets conn->result
  * to a PGresult with status PGRES_FATAL_ERROR, and stores the current
- * contents of conn->errorMessage into that result.  It differs from a
- * plain call on PQmakeEmptyPGresult() in that if there is already an
- * async result with status PGRES_FATAL_ERROR, the current error message
- * is APPENDED to the old error message instead of replacing it.  This
- * behavior lets us report multiple error conditions properly, if necessary.
- * (An example where this is needed is when the backend sends an 'E' message
- * and immediately closes the connection --- we want to report both the
- * backend error and the connection closure error.)
+ * contents of conn->errorMessage into that result.
  */
 void
 pqSaveErrorResult(PGconn *conn)
 {
-    /*
-     * If no old async result, just let PQmakeEmptyPGresult make one. Likewise
-     * if old result is not an error message.
-     */
-    if (conn->result == NULL ||
-        conn->result->resultStatus != PGRES_FATAL_ERROR ||
-        conn->result->errMsg == NULL)
-    {
-        pqClearAsyncResult(conn);
-        conn->result = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR);
-    }
-    else
-    {
-        /* Else, concatenate error message to existing async result. */
-        pqCatenateResultError(conn->result, conn->errorMessage.data);
-    }
+    pqClearAsyncResult(conn);
+    conn->result = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR);
 }

 /*
- * As above, and append conn->write_err_msg to whatever other error we have.
- * This is used when we've detected a write failure and have exhausted our
- * chances of reporting something else instead.
+ * As above, after appending conn->write_err_msg to whatever other error we
+ * have.  This is used when we've detected a write failure and have exhausted
+ * our chances of reporting something else instead.
  */
 static void
 pqSaveWriteError(PGconn *conn)
 {
     /*
-     * Ensure conn->result is an error result, and add anything in
-     * conn->errorMessage to it.
+     * If write_err_msg is null because of previous strdup failure, do what we
+     * can.  (It's likely our machinations here will get OOM failures as well,
+     * but might as well try.)
      */
-    pqSaveErrorResult(conn);
-
-    /*
-     * Now append write_err_msg to that.  If it's null because of previous
-     * strdup failure, do what we can.  (It's likely our machinations here are
-     * all getting OOM failures as well, but ...)
-     */
-    if (conn->write_err_msg && conn->write_err_msg[0] != '\0')
-        pqCatenateResultError(conn->result, conn->write_err_msg);
+    if (conn->write_err_msg)
+    {
+        appendPQExpBufferStr(&conn->errorMessage, conn->write_err_msg);
+        /* Avoid possibly appending the same message twice */
+        conn->write_err_msg[0] = '\0';
+    }
     else
-        pqCatenateResultError(conn->result,
-                              libpq_gettext("write to server failed\n"));
+        appendPQExpBufferStr(&conn->errorMessage,
+                             libpq_gettext("write to server failed\n"));
+
+    pqSaveErrorResult(conn);
 }

 /*
  * This subroutine prepares an async result object for return to the caller.
  * If there is not already an async result object, build an error object
  * using whatever is in conn->errorMessage.  In any case, clear the async
- * result storage and make sure PQerrorMessage will agree with the result's
- * error string.
+ * result storage.
  */
 PGresult *
 pqPrepareAsyncResult(PGconn *conn)
@@ -835,16 +795,6 @@ pqPrepareAsyncResult(PGconn *conn)
     res = conn->result;
     if (!res)
         res = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR);
-    else
-    {
-        /*
-         * Make sure PQerrorMessage agrees with result; it could be different
-         * if we have concatenated messages.
-         */
-        resetPQExpBuffer(&conn->errorMessage);
-        appendPQExpBufferStr(&conn->errorMessage,
-                             PQresultErrorMessage(res));
-    }

     /*
      * Replace conn->result with next_result, if any.  In the normal case
@@ -1229,17 +1179,32 @@ fail:
  *
  * Returns: 1 if successfully submitted
  *            0 if error (conn->errorMessage is set)
+ *
+ * PQsendQueryContinue is a non-exported version that behaves identically
+ * except that it doesn't reset conn->errorMessage.
  */
 int
 PQsendQuery(PGconn *conn, const char *query)
 {
-    if (!PQsendQueryStart(conn))
+    return PQsendQueryInternal(conn, query, true);
+}
+
+int
+PQsendQueryContinue(PGconn *conn, const char *query)
+{
+    return PQsendQueryInternal(conn, query, false);
+}
+
+static int
+PQsendQueryInternal(PGconn *conn, const char *query, bool newQuery)
+{
+    if (!PQsendQueryStart(conn, newQuery))
         return 0;

     /* check the argument */
     if (!query)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("command string is a null pointer\n"));
         return 0;
     }
@@ -1291,19 +1256,19 @@ PQsendQueryParams(PGconn *conn,
                   const int *paramFormats,
                   int resultFormat)
 {
-    if (!PQsendQueryStart(conn))
+    if (!PQsendQueryStart(conn, true))
         return 0;

     /* check the arguments */
     if (!command)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("command string is a null pointer\n"));
         return 0;
     }
     if (nParams < 0 || nParams > 65535)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("number of parameters must be between 0 and 65535\n"));
         return 0;
     }
@@ -1331,25 +1296,25 @@ PQsendPrepare(PGconn *conn,
               const char *stmtName, const char *query,
               int nParams, const Oid *paramTypes)
 {
-    if (!PQsendQueryStart(conn))
+    if (!PQsendQueryStart(conn, true))
         return 0;

     /* check the arguments */
     if (!stmtName)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("statement name is a null pointer\n"));
         return 0;
     }
     if (!query)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("command string is a null pointer\n"));
         return 0;
     }
     if (nParams < 0 || nParams > 65535)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("number of parameters must be between 0 and 65535\n"));
         return 0;
     }
@@ -1357,7 +1322,7 @@ PQsendPrepare(PGconn *conn,
     /* This isn't gonna work on a 2.0 server */
     if (PG_PROTOCOL_MAJOR(conn->pversion) < 3)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("function requires at least protocol version 3.0\n"));
         return 0;
     }
@@ -1432,19 +1397,19 @@ PQsendQueryPrepared(PGconn *conn,
                     const int *paramFormats,
                     int resultFormat)
 {
-    if (!PQsendQueryStart(conn))
+    if (!PQsendQueryStart(conn, true))
         return 0;

     /* check the arguments */
     if (!stmtName)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("statement name is a null pointer\n"));
         return 0;
     }
     if (nParams < 0 || nParams > 65535)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("number of parameters must be between 0 and 65535\n"));
         return 0;
     }
@@ -1464,25 +1429,28 @@ PQsendQueryPrepared(PGconn *conn,
  * Common startup code for PQsendQuery and sibling routines
  */
 static bool
-PQsendQueryStart(PGconn *conn)
+PQsendQueryStart(PGconn *conn, bool newQuery)
 {
     if (!conn)
         return false;

-    /* clear the error string */
-    resetPQExpBuffer(&conn->errorMessage);
+    /*
+     * If this is the beginning of a query cycle, reset the error buffer.
+     */
+    if (newQuery)
+        resetPQExpBuffer(&conn->errorMessage);

     /* Don't try to send if we know there's no live connection. */
     if (conn->status != CONNECTION_OK)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("no connection to the server\n"));
         return false;
     }
     /* Can't send while already busy, either. */
     if (conn->asyncStatus != PGASYNC_IDLE)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("another command is already in progress\n"));
         return false;
     }
@@ -1520,7 +1488,7 @@ PQsendQueryGuts(PGconn *conn,
     /* This isn't gonna work on a 2.0 server */
     if (PG_PROTOCOL_MAJOR(conn->pversion) < 3)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("function requires at least protocol version 3.0\n"));
         return 0;
     }
@@ -1596,7 +1564,7 @@ PQsendQueryGuts(PGconn *conn,
                     nbytes = paramLengths[i];
                 else
                 {
-                    printfPQExpBuffer(&conn->errorMessage,
+                    appendPQExpBuffer(&conn->errorMessage,
                                       libpq_gettext("length must be given for binary parameter\n"));
                     goto sendFailed;
                 }
@@ -1859,7 +1827,7 @@ PQgetResult(PGconn *conn)
             res = getCopyResult(conn, PGRES_COPY_BOTH);
             break;
         default:
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("unexpected asyncStatus: %d\n"),
                               (int) conn->asyncStatus);
             res = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR);
@@ -1879,7 +1847,7 @@ PQgetResult(PGconn *conn)
             if (!res->events[i].proc(PGEVT_RESULTCREATE, &evt,
                                      res->events[i].passThrough))
             {
-                printfPQExpBuffer(&conn->errorMessage,
+                appendPQExpBuffer(&conn->errorMessage,
                                   libpq_gettext("PGEventProc \"%s\" failed during PGEVT_RESULTCREATE event\n"),
                                   res->events[i].name);
                 pqSetResultError(res, conn->errorMessage.data);
@@ -2025,6 +1993,11 @@ PQexecStart(PGconn *conn)
     if (!conn)
         return false;

+    /*
+     * Since this is the beginning of a query cycle, reset the error buffer.
+     */
+    resetPQExpBuffer(&conn->errorMessage);
+
     /*
      * Silently discard any prior query result that application didn't eat.
      * This is probably poor design, but it's here for backward compatibility.
@@ -2047,7 +2020,7 @@ PQexecStart(PGconn *conn)
             else
             {
                 /* In older protocols we have to punt */
-                printfPQExpBuffer(&conn->errorMessage,
+                appendPQExpBuffer(&conn->errorMessage,
                                   libpq_gettext("COPY IN state must be terminated first\n"));
                 return false;
             }
@@ -2067,7 +2040,7 @@ PQexecStart(PGconn *conn)
             else
             {
                 /* In older protocols we have to punt */
-                printfPQExpBuffer(&conn->errorMessage,
+                appendPQExpBuffer(&conn->errorMessage,
                                   libpq_gettext("COPY OUT state must be terminated first\n"));
                 return false;
             }
@@ -2075,7 +2048,7 @@ PQexecStart(PGconn *conn)
         else if (resultStatus == PGRES_COPY_BOTH)
         {
             /* We don't allow PQexec during COPY BOTH */
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("PQexec not allowed during COPY BOTH\n"));
             return false;
         }
@@ -2099,8 +2072,9 @@ PQexecFinish(PGconn *conn)

     /*
      * For backwards compatibility, return the last result if there are more
-     * than one --- but merge error messages if we get more than one error
-     * result.
+     * than one.  (We used to have logic here to concatenate successive error
+     * messages, but now that happens automatically, since conn->errorMessage
+     * will continue to accumulate errors throughout this loop.)
      *
      * We have to stop if we see copy in/out/both, however. We will resume
      * parsing after application performs the data transfer.
@@ -2111,23 +2085,7 @@ PQexecFinish(PGconn *conn)
     while ((result = PQgetResult(conn)) != NULL)
     {
         if (lastResult)
-        {
-            if (lastResult->resultStatus == PGRES_FATAL_ERROR &&
-                result->resultStatus == PGRES_FATAL_ERROR)
-            {
-                pqCatenateResultError(lastResult, result->errMsg);
-                PQclear(result);
-                result = lastResult;
-
-                /*
-                 * Make sure PQerrorMessage agrees with concatenated result
-                 */
-                resetPQExpBuffer(&conn->errorMessage);
-                appendPQExpBufferStr(&conn->errorMessage, result->errMsg);
-            }
-            else
-                PQclear(lastResult);
-        }
+            PQclear(lastResult);
         lastResult = result;
         if (result->resultStatus == PGRES_COPY_IN ||
             result->resultStatus == PGRES_COPY_OUT ||
@@ -2223,13 +2181,13 @@ PQsendDescribe(PGconn *conn, char desc_type, const char *desc_target)
     if (!desc_target)
         desc_target = "";

-    if (!PQsendQueryStart(conn))
+    if (!PQsendQueryStart(conn, true))
         return 0;

     /* This isn't gonna work on a 2.0 server */
     if (PG_PROTOCOL_MAJOR(conn->pversion) < 3)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("function requires at least protocol version 3.0\n"));
         return 0;
     }
@@ -2321,7 +2279,7 @@ PQputCopyData(PGconn *conn, const char *buffer, int nbytes)
     if (conn->asyncStatus != PGASYNC_COPY_IN &&
         conn->asyncStatus != PGASYNC_COPY_BOTH)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("no COPY in progress\n"));
         return -1;
     }
@@ -2388,7 +2346,7 @@ PQputCopyEnd(PGconn *conn, const char *errormsg)
     if (conn->asyncStatus != PGASYNC_COPY_IN &&
         conn->asyncStatus != PGASYNC_COPY_BOTH)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("no COPY in progress\n"));
         return -1;
     }
@@ -2431,7 +2389,7 @@ PQputCopyEnd(PGconn *conn, const char *errormsg)
         if (errormsg)
         {
             /* Oops, no way to do this in 2.0 */
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("function requires at least protocol version 3.0\n"));
             return -1;
         }
@@ -2450,7 +2408,6 @@ PQputCopyEnd(PGconn *conn, const char *errormsg)
         conn->asyncStatus = PGASYNC_COPY_OUT;
     else
         conn->asyncStatus = PGASYNC_BUSY;
-    resetPQExpBuffer(&conn->errorMessage);

     /* Try to flush data */
     if (pqFlush(conn) < 0)
@@ -2478,7 +2435,7 @@ PQgetCopyData(PGconn *conn, char **buffer, int async)
     if (conn->asyncStatus != PGASYNC_COPY_OUT &&
         conn->asyncStatus != PGASYNC_COPY_BOTH)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("no COPY in progress\n"));
         return -2;
     }
@@ -2662,13 +2619,15 @@ PQfn(PGconn *conn,
     if (!conn)
         return NULL;

-    /* clear the error string */
+    /*
+     * Since this is the beginning of a query cycle, reset the error buffer.
+     */
     resetPQExpBuffer(&conn->errorMessage);

     if (conn->sock == PGINVALID_SOCKET || conn->asyncStatus != PGASYNC_IDLE ||
         conn->result != NULL)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("connection in wrong state\n"));
         return NULL;
     }
@@ -3246,7 +3205,11 @@ PQsetnonblocking(PGconn *conn, int arg)
      * need to flush the send queue at this point in order to guarantee proper
      * behavior. this is ok because either they are making a transition _from_
      * or _to_ blocking mode, either way we can block them.
+     *
+     * Clear errorMessage in case pqFlush adds to it.
      */
+    resetPQExpBuffer(&conn->errorMessage);
+
     /* if we are going from blocking to non-blocking flush here */
     if (pqFlush(conn))
         return -1;
@@ -3388,7 +3351,7 @@ PQescapeStringInternal(PGconn *conn,
             if (error)
                 *error = 1;
             if (conn)
-                printfPQExpBuffer(&conn->errorMessage,
+                appendPQExpBuffer(&conn->errorMessage,
                                   libpq_gettext("incomplete multibyte character\n"));
             for (; i < len; i++)
             {
@@ -3419,6 +3382,9 @@ PQescapeStringConn(PGconn *conn,
             *error = 1;
         return 0;
     }
+
+    resetPQExpBuffer(&conn->errorMessage);
+
     return PQescapeStringInternal(conn, to, from, length, error,
                                   conn->client_encoding,
                                   conn->std_strings);
@@ -3455,6 +3421,8 @@ PQescapeInternal(PGconn *conn, const char *str, size_t len, bool as_ident)
     if (!conn)
         return NULL;

+    resetPQExpBuffer(&conn->errorMessage);
+
     /* Scan the string for characters that must be escaped. */
     for (s = str; (s - str) < len && *s != '\0'; ++s)
     {
@@ -3472,7 +3440,7 @@ PQescapeInternal(PGconn *conn, const char *str, size_t len, bool as_ident)
             /* Multibyte character overruns allowable length. */
             if ((s - str) + charlen > len || memchr(s, 0, charlen) != NULL)
             {
-                printfPQExpBuffer(&conn->errorMessage,
+                appendPQExpBuffer(&conn->errorMessage,
                                   libpq_gettext("incomplete multibyte character\n"));
                 return NULL;
             }
@@ -3490,7 +3458,7 @@ PQescapeInternal(PGconn *conn, const char *str, size_t len, bool as_ident)
     result = rp = (char *) malloc(result_size);
     if (rp == NULL)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("out of memory\n"));
         return NULL;
     }
@@ -3655,7 +3623,7 @@ PQescapeByteaInternal(PGconn *conn,
     if (rp == NULL)
     {
         if (conn)
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("out of memory\n"));
         return NULL;
     }
@@ -3717,6 +3685,9 @@ PQescapeByteaConn(PGconn *conn,
 {
     if (!conn)
         return NULL;
+
+    resetPQExpBuffer(&conn->errorMessage);
+
     return PQescapeByteaInternal(conn, from, from_length, to_length,
                                  conn->std_strings,
                                  (conn->sversion >= 90000));
diff --git a/src/interfaces/libpq/fe-gssapi-common.c b/src/interfaces/libpq/fe-gssapi-common.c
index c2e79bb55c..b7661dd659 100644
--- a/src/interfaces/libpq/fe-gssapi-common.c
+++ b/src/interfaces/libpq/fe-gssapi-common.c
@@ -46,7 +46,6 @@ void
 pg_GSS_error(const char *mprefix, PGconn *conn,
              OM_uint32 maj_stat, OM_uint32 min_stat)
 {
-    resetPQExpBuffer(&conn->errorMessage);
     appendPQExpBuffer(&conn->errorMessage, "%s:", mprefix);
     pg_GSS_error_int(&conn->errorMessage, maj_stat, GSS_C_GSS_CODE);
     appendPQExpBufferChar(&conn->errorMessage, ':');
@@ -94,7 +93,7 @@ pg_GSS_load_servicename(PGconn *conn)
     host = PQhost(conn);
     if (!(host && host[0] != '\0'))
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("host name must be specified\n"));
         return STATUS_ERROR;
     }
@@ -107,7 +106,7 @@ pg_GSS_load_servicename(PGconn *conn)
     temp_gbuf.value = (char *) malloc(maxlen);
     if (!temp_gbuf.value)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("out of memory\n"));
         return STATUS_ERROR;
     }
diff --git a/src/interfaces/libpq/fe-lobj.c b/src/interfaces/libpq/fe-lobj.c
index 432935061f..5a6e4df3a0 100644
--- a/src/interfaces/libpq/fe-lobj.c
+++ b/src/interfaces/libpq/fe-lobj.c
@@ -61,11 +61,8 @@ lo_open(PGconn *conn, Oid lobjId, int mode)
     PQArgBlock    argv[2];
     PGresult   *res;

-    if (conn == NULL || conn->lobjfuncs == NULL)
-    {
-        if (lo_initialize(conn) < 0)
-            return -1;
-    }
+    if (lo_initialize(conn) < 0)
+        return -1;

     argv[0].isint = 1;
     argv[0].len = 4;
@@ -103,11 +100,8 @@ lo_close(PGconn *conn, int fd)
     int            retval;
     int            result_len;

-    if (conn == NULL || conn->lobjfuncs == NULL)
-    {
-        if (lo_initialize(conn) < 0)
-            return -1;
-    }
+    if (lo_initialize(conn) < 0)
+        return -1;

     argv[0].isint = 1;
     argv[0].len = 4;
@@ -141,16 +135,13 @@ lo_truncate(PGconn *conn, int fd, size_t len)
     int            retval;
     int            result_len;

-    if (conn == NULL || conn->lobjfuncs == NULL)
-    {
-        if (lo_initialize(conn) < 0)
-            return -1;
-    }
+    if (lo_initialize(conn) < 0)
+        return -1;

     /* Must check this on-the-fly because it's not there pre-8.3 */
     if (conn->lobjfuncs->fn_lo_truncate == 0)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("cannot determine OID of function lo_truncate\n"));
         return -1;
     }
@@ -166,7 +157,7 @@ lo_truncate(PGconn *conn, int fd, size_t len)
      */
     if (len > (size_t) INT_MAX)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("argument of lo_truncate exceeds integer range\n"));
         return -1;
     }
@@ -209,15 +200,12 @@ lo_truncate64(PGconn *conn, int fd, pg_int64 len)
     int            retval;
     int            result_len;

-    if (conn == NULL || conn->lobjfuncs == NULL)
-    {
-        if (lo_initialize(conn) < 0)
-            return -1;
-    }
+    if (lo_initialize(conn) < 0)
+        return -1;

     if (conn->lobjfuncs->fn_lo_truncate64 == 0)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("cannot determine OID of function lo_truncate64\n"));
         return -1;
     }
@@ -261,11 +249,8 @@ lo_read(PGconn *conn, int fd, char *buf, size_t len)
     PGresult   *res;
     int            result_len;

-    if (conn == NULL || conn->lobjfuncs == NULL)
-    {
-        if (lo_initialize(conn) < 0)
-            return -1;
-    }
+    if (lo_initialize(conn) < 0)
+        return -1;

     /*
      * Long ago, somebody thought it'd be a good idea to declare this function
@@ -275,7 +260,7 @@ lo_read(PGconn *conn, int fd, char *buf, size_t len)
      */
     if (len > (size_t) INT_MAX)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("argument of lo_read exceeds integer range\n"));
         return -1;
     }
@@ -316,11 +301,8 @@ lo_write(PGconn *conn, int fd, const char *buf, size_t len)
     int            result_len;
     int            retval;

-    if (conn == NULL || conn->lobjfuncs == NULL)
-    {
-        if (lo_initialize(conn) < 0)
-            return -1;
-    }
+    if (lo_initialize(conn) < 0)
+        return -1;

     /*
      * Long ago, somebody thought it'd be a good idea to declare this function
@@ -330,7 +312,7 @@ lo_write(PGconn *conn, int fd, const char *buf, size_t len)
      */
     if (len > (size_t) INT_MAX)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("argument of lo_write exceeds integer range\n"));
         return -1;
     }
@@ -369,11 +351,8 @@ lo_lseek(PGconn *conn, int fd, int offset, int whence)
     int            retval;
     int            result_len;

-    if (conn == NULL || conn->lobjfuncs == NULL)
-    {
-        if (lo_initialize(conn) < 0)
-            return -1;
-    }
+    if (lo_initialize(conn) < 0)
+        return -1;

     argv[0].isint = 1;
     argv[0].len = 4;
@@ -413,15 +392,12 @@ lo_lseek64(PGconn *conn, int fd, pg_int64 offset, int whence)
     pg_int64    retval;
     int            result_len;

-    if (conn == NULL || conn->lobjfuncs == NULL)
-    {
-        if (lo_initialize(conn) < 0)
-            return -1;
-    }
+    if (lo_initialize(conn) < 0)
+        return -1;

     if (conn->lobjfuncs->fn_lo_lseek64 == 0)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("cannot determine OID of function lo_lseek64\n"));
         return -1;
     }
@@ -469,11 +445,8 @@ lo_creat(PGconn *conn, int mode)
     int            retval;
     int            result_len;

-    if (conn == NULL || conn->lobjfuncs == NULL)
-    {
-        if (lo_initialize(conn) < 0)
-            return InvalidOid;
-    }
+    if (lo_initialize(conn) < 0)
+        return InvalidOid;

     argv[0].isint = 1;
     argv[0].len = 4;
@@ -508,16 +481,13 @@ lo_create(PGconn *conn, Oid lobjId)
     int            retval;
     int            result_len;

-    if (conn == NULL || conn->lobjfuncs == NULL)
-    {
-        if (lo_initialize(conn) < 0)
-            return InvalidOid;
-    }
+    if (lo_initialize(conn) < 0)
+        return InvalidOid;

     /* Must check this on-the-fly because it's not there pre-8.1 */
     if (conn->lobjfuncs->fn_lo_create == 0)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("cannot determine OID of function lo_create\n"));
         return InvalidOid;
     }
@@ -552,11 +522,8 @@ lo_tell(PGconn *conn, int fd)
     PGresult   *res;
     int            result_len;

-    if (conn == NULL || conn->lobjfuncs == NULL)
-    {
-        if (lo_initialize(conn) < 0)
-            return -1;
-    }
+    if (lo_initialize(conn) < 0)
+        return -1;

     argv[0].isint = 1;
     argv[0].len = 4;
@@ -588,15 +555,12 @@ lo_tell64(PGconn *conn, int fd)
     PGresult   *res;
     int            result_len;

-    if (conn == NULL || conn->lobjfuncs == NULL)
-    {
-        if (lo_initialize(conn) < 0)
-            return -1;
-    }
+    if (lo_initialize(conn) < 0)
+        return -1;

     if (conn->lobjfuncs->fn_lo_tell64 == 0)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("cannot determine OID of function lo_tell64\n"));
         return -1;
     }
@@ -632,11 +596,8 @@ lo_unlink(PGconn *conn, Oid lobjId)
     int            result_len;
     int            retval;

-    if (conn == NULL || conn->lobjfuncs == NULL)
-    {
-        if (lo_initialize(conn) < 0)
-            return -1;
-    }
+    if (lo_initialize(conn) < 0)
+        return -1;

     argv[0].isint = 1;
     argv[0].len = 4;
@@ -696,13 +657,19 @@ lo_import_internal(PGconn *conn, const char *filename, Oid oid)
     int            lobj;
     char        sebuf[PG_STRERROR_R_BUFLEN];

+    if (conn == NULL)
+        return InvalidOid;
+
+    /* Since this is the beginning of a query cycle, reset the error buffer */
+    resetPQExpBuffer(&conn->errorMessage);
+
     /*
      * open the file to be read in
      */
     fd = open(filename, O_RDONLY | PG_BINARY, 0666);
     if (fd < 0)
     {                            /* error */
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("could not open file \"%s\": %s\n"),
                           filename, strerror_r(errno, sebuf, sizeof(sebuf)));
         return InvalidOid;
@@ -757,6 +724,7 @@ lo_import_internal(PGconn *conn, const char *filename, Oid oid)

         (void) lo_close(conn, lobj);
         (void) close(fd);
+        /* deliberately overwrite any error from lo_close */
         printfPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("could not read from file \"%s\": %s\n"),
                           filename,
@@ -811,6 +779,7 @@ lo_export(PGconn *conn, Oid lobjId, const char *filename)
         int            save_errno = errno;

         (void) lo_close(conn, lobj);
+        /* deliberately overwrite any error from lo_close */
         printfPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("could not open file \"%s\": %s\n"),
                           filename,
@@ -831,6 +800,7 @@ lo_export(PGconn *conn, Oid lobjId, const char *filename)

             (void) lo_close(conn, lobj);
             (void) close(fd);
+            /* deliberately overwrite any error from lo_close */
             printfPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("could not write to file \"%s\": %s\n"),
                               filename,
@@ -855,7 +825,7 @@ lo_export(PGconn *conn, Oid lobjId, const char *filename)
     /* if we already failed, don't overwrite that msg with a close error */
     if (close(fd) != 0 && result >= 0)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("could not write to file \"%s\": %s\n"),
                           filename, strerror_r(errno, sebuf, sizeof(sebuf)));
         result = -1;
@@ -868,9 +838,11 @@ lo_export(PGconn *conn, Oid lobjId, const char *filename)
 /*
  * lo_initialize
  *
- * Initialize the large object interface for an existing connection.
- * We ask the backend about the functions OID's in pg_proc for all
- * functions that are required for large object operations.
+ * Initialize for a new large-object operation on an existing connection.
+ * Return 0 if OK, -1 on failure.
+ *
+ * If we haven't previously done so, we collect the function OIDs from
+ * pg_proc for all functions that are required for large object operations.
  */
 static int
 lo_initialize(PGconn *conn)
@@ -882,16 +854,25 @@ lo_initialize(PGconn *conn)
     const char *fname;
     Oid            foid;

-    if (!conn)
+    /* Nothing we can do with no connection */
+    if (conn == NULL)
         return -1;

+    /* Since this is the beginning of a query cycle, reset the error buffer */
+    resetPQExpBuffer(&conn->errorMessage);
+
+    /* Nothing else to do if we already collected info */
+    if (conn->lobjfuncs != NULL)
+        return 0;
+
     /*
-     * Allocate the structure to hold the functions OID's
+     * Allocate the structure to hold the function OIDs.  We don't store it
+     * into the PGconn until it's successfully filled.
      */
     lobjfuncs = (PGlobjfuncs *) malloc(sizeof(PGlobjfuncs));
     if (lobjfuncs == NULL)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("out of memory\n"));
         return -1;
     }
@@ -942,7 +923,7 @@ lo_initialize(PGconn *conn)
     {
         free(lobjfuncs);
         PQclear(res);
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("query to initialize large object functions did not return data\n"));
         return -1;
     }
@@ -991,56 +972,56 @@ lo_initialize(PGconn *conn)
      */
     if (lobjfuncs->fn_lo_open == 0)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("cannot determine OID of function lo_open\n"));
         free(lobjfuncs);
         return -1;
     }
     if (lobjfuncs->fn_lo_close == 0)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("cannot determine OID of function lo_close\n"));
         free(lobjfuncs);
         return -1;
     }
     if (lobjfuncs->fn_lo_creat == 0)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("cannot determine OID of function lo_creat\n"));
         free(lobjfuncs);
         return -1;
     }
     if (lobjfuncs->fn_lo_unlink == 0)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("cannot determine OID of function lo_unlink\n"));
         free(lobjfuncs);
         return -1;
     }
     if (lobjfuncs->fn_lo_lseek == 0)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("cannot determine OID of function lo_lseek\n"));
         free(lobjfuncs);
         return -1;
     }
     if (lobjfuncs->fn_lo_tell == 0)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("cannot determine OID of function lo_tell\n"));
         free(lobjfuncs);
         return -1;
     }
     if (lobjfuncs->fn_lo_read == 0)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("cannot determine OID of function loread\n"));
         free(lobjfuncs);
         return -1;
     }
     if (lobjfuncs->fn_lo_write == 0)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("cannot determine OID of function lowrite\n"));
         free(lobjfuncs);
         return -1;
diff --git a/src/interfaces/libpq/fe-misc.c b/src/interfaces/libpq/fe-misc.c
index 6094f048f3..9d57f21119 100644
--- a/src/interfaces/libpq/fe-misc.c
+++ b/src/interfaces/libpq/fe-misc.c
@@ -379,7 +379,7 @@ pqCheckOutBufferSpace(size_t bytes_needed, PGconn *conn)
     }

     /* realloc failed. Probably out of memory */
-    printfPQExpBuffer(&conn->errorMessage,
+    appendPQExpBuffer(&conn->errorMessage,
                       "cannot allocate memory for output buffer\n");
     return EOF;
 }
@@ -473,7 +473,7 @@ pqCheckInBufferSpace(size_t bytes_needed, PGconn *conn)
     }

     /* realloc failed. Probably out of memory */
-    printfPQExpBuffer(&conn->errorMessage,
+    appendPQExpBuffer(&conn->errorMessage,
                       "cannot allocate memory for input buffer\n");
     return EOF;
 }
@@ -619,7 +619,7 @@ pqReadData(PGconn *conn)

     if (conn->sock == PGINVALID_SOCKET)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("connection not open\n"));
         return -1;
     }
@@ -798,7 +798,7 @@ retry4:
      * means the connection has been closed.  Cope.
      */
 definitelyEOF:
-    printfPQExpBuffer(&conn->errorMessage,
+    appendPQExpBuffer(&conn->errorMessage,
                       libpq_gettext("server closed the connection unexpectedly\n"
                                     "\tThis probably means the server terminated abnormally\n"
                                     "\tbefore or while processing the request.\n"));
@@ -836,6 +836,7 @@ pqSendSome(PGconn *conn, int len)
 {
     char       *ptr = conn->outBuffer;
     int            remaining = conn->outCount;
+    int            oldmsglen = conn->errorMessage.len;
     int            result = 0;

     /*
@@ -862,13 +863,10 @@ pqSendSome(PGconn *conn, int len)

     if (conn->sock == PGINVALID_SOCKET)
     {
-        printfPQExpBuffer(&conn->errorMessage,
-                          libpq_gettext("connection not open\n"));
         conn->write_failed = true;
-        /* Transfer error message to conn->write_err_msg, if possible */
+        /* Insert error message into conn->write_err_msg, if possible */
         /* (strdup failure is OK, we'll cope later) */
-        conn->write_err_msg = strdup(conn->errorMessage.data);
-        resetPQExpBuffer(&conn->errorMessage);
+        conn->write_err_msg = strdup(libpq_gettext("connection not open\n"));
         /* Discard queued data; no chance it'll ever be sent */
         conn->outCount = 0;
         return 0;
@@ -915,14 +913,16 @@ pqSendSome(PGconn *conn, int len)
                      * Transfer error message to conn->write_err_msg, if
                      * possible (strdup failure is OK, we'll cope later).
                      *
-                     * Note: this assumes that pqsecure_write and its children
-                     * will overwrite not append to conn->errorMessage.  If
-                     * that's ever changed, we could remember the length of
-                     * conn->errorMessage at entry to this routine, and then
-                     * save and delete just what was appended.
+                     * We only want to transfer whatever has been appended to
+                     * conn->errorMessage since we entered this routine.
                      */
-                    conn->write_err_msg = strdup(conn->errorMessage.data);
-                    resetPQExpBuffer(&conn->errorMessage);
+                    if (!PQExpBufferBroken(&conn->errorMessage))
+                    {
+                        conn->write_err_msg = strdup(conn->errorMessage.data +
+                                                     oldmsglen);
+                        conn->errorMessage.len = oldmsglen;
+                        conn->errorMessage.data[oldmsglen] = '\0';
+                    }

                     /* Discard queued data; no chance it'll ever be sent */
                     conn->outCount = 0;
@@ -1056,7 +1056,7 @@ pqWaitTimed(int forRead, int forWrite, PGconn *conn, time_t finish_time)

     if (result == 0)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("timeout expired\n"));
         return 1;
     }
@@ -1101,7 +1101,7 @@ pqSocketCheck(PGconn *conn, int forRead, int forWrite, time_t end_time)
         return -1;
     if (conn->sock == PGINVALID_SOCKET)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("invalid socket\n"));
         return -1;
     }
@@ -1124,7 +1124,7 @@ pqSocketCheck(PGconn *conn, int forRead, int forWrite, time_t end_time)
     {
         char        sebuf[PG_STRERROR_R_BUFLEN];

-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("select() failed: %s\n"),
                           SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
     }
diff --git a/src/interfaces/libpq/fe-protocol2.c b/src/interfaces/libpq/fe-protocol2.c
index ad6587f924..fa0614a92b 100644
--- a/src/interfaces/libpq/fe-protocol2.c
+++ b/src/interfaces/libpq/fe-protocol2.c
@@ -83,7 +83,7 @@ pqSetenvPoll(PGconn *conn)
             return PGRES_POLLING_OK;

         default:
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("invalid setenv state %c, probably indicative of memory corruption\n"),
                               conn->setenv_state);
             goto error_return;
@@ -380,7 +380,7 @@ pqSetenvPoll(PGconn *conn)
                 }

             default:
-                printfPQExpBuffer(&conn->errorMessage,
+                appendPQExpBuffer(&conn->errorMessage,
                                   libpq_gettext("invalid state %c, "
                                                 "probably indicative of memory corruption\n"),
                                   conn->setenv_state);
@@ -493,7 +493,7 @@ pqParseInput2(PGconn *conn)
                                                            PGRES_COMMAND_OK);
                         if (!conn->result)
                         {
-                            printfPQExpBuffer(&conn->errorMessage,
+                            appendPQExpBuffer(&conn->errorMessage,
                                               libpq_gettext("out of memory"));
                             pqSaveErrorResult(conn);
                         }
@@ -528,7 +528,7 @@ pqParseInput2(PGconn *conn)
                                                            PGRES_EMPTY_QUERY);
                         if (!conn->result)
                         {
-                            printfPQExpBuffer(&conn->errorMessage,
+                            appendPQExpBuffer(&conn->errorMessage,
                                               libpq_gettext("out of memory"));
                             pqSaveErrorResult(conn);
                         }
@@ -622,7 +622,7 @@ pqParseInput2(PGconn *conn)
                      * never arrives from the server during protocol 2.0.
                      */
                 default:
-                    printfPQExpBuffer(&conn->errorMessage,
+                    appendPQExpBuffer(&conn->errorMessage,
                                       libpq_gettext("unexpected response from server; first received character was
\"%c\"\n"),
                                       id);
                     /* build an error result holding the error message */
@@ -754,7 +754,7 @@ advance_and_error:
     if (!errmsg)
         errmsg = libpq_gettext("out of memory for query result");

-    printfPQExpBuffer(&conn->errorMessage, "%s\n", errmsg);
+    appendPQExpBuffer(&conn->errorMessage, "%s\n", errmsg);

     /*
      * XXX: if PQmakeEmptyPGresult() fails, there's probably not much we can
@@ -929,7 +929,7 @@ set_error_result:
     if (!errmsg)
         errmsg = libpq_gettext("out of memory for query result");

-    printfPQExpBuffer(&conn->errorMessage, "%s\n", errmsg);
+    appendPQExpBuffer(&conn->errorMessage, "%s\n", errmsg);

     /*
      * XXX: if PQmakeEmptyPGresult() fails, there's probably not much we can
@@ -1042,11 +1042,10 @@ pqGetErrorNotice2(PGconn *conn, bool isError)
     {
         pqClearAsyncResult(conn);    /* redundant, but be safe */
         conn->result = res;
-        resetPQExpBuffer(&conn->errorMessage);
         if (res && !PQExpBufferDataBroken(workBuf) && res->errMsg)
             appendPQExpBufferStr(&conn->errorMessage, res->errMsg);
         else
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("out of memory"));
         if (conn->xactStatus == PQTRANS_INTRANS)
             conn->xactStatus = PQTRANS_INERROR;
@@ -1203,7 +1202,7 @@ pqGetCopyData2(PGconn *conn, char **buffer, int async)
         *buffer = (char *) malloc(msgLength + 1);
         if (*buffer == NULL)
         {
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("out of memory\n"));
             return -2;
         }
@@ -1349,7 +1348,7 @@ pqEndcopy2(PGconn *conn)
     if (conn->asyncStatus != PGASYNC_COPY_IN &&
         conn->asyncStatus != PGASYNC_COPY_OUT)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("no COPY in progress\n"));
         return 1;
     }
@@ -1367,7 +1366,6 @@ pqEndcopy2(PGconn *conn)

     /* Return to active duty */
     conn->asyncStatus = PGASYNC_BUSY;
-    resetPQExpBuffer(&conn->errorMessage);

     /* Wait for the completion response */
     result = PQgetResult(conn);
@@ -1526,7 +1524,7 @@ pqFunctionCall2(PGconn *conn, Oid fnid,
                 else
                 {
                     /* The backend violates the protocol. */
-                    printfPQExpBuffer(&conn->errorMessage,
+                    appendPQExpBuffer(&conn->errorMessage,
                                       libpq_gettext("protocol error: id=0x%x\n"),
                                       id);
                     pqSaveErrorResult(conn);
@@ -1558,7 +1556,7 @@ pqFunctionCall2(PGconn *conn, Oid fnid,
                 return PQmakeEmptyPGresult(conn, status);
             default:
                 /* The backend violates the protocol. */
-                printfPQExpBuffer(&conn->errorMessage,
+                appendPQExpBuffer(&conn->errorMessage,
                                   libpq_gettext("protocol error: id=0x%x\n"),
                                   id);
                 pqSaveErrorResult(conn);
diff --git a/src/interfaces/libpq/fe-protocol3.c b/src/interfaces/libpq/fe-protocol3.c
index a4d6ee2674..13cf7bc8c4 100644
--- a/src/interfaces/libpq/fe-protocol3.c
+++ b/src/interfaces/libpq/fe-protocol3.c
@@ -202,7 +202,7 @@ pqParseInput3(PGconn *conn)
                                                            PGRES_COMMAND_OK);
                         if (!conn->result)
                         {
-                            printfPQExpBuffer(&conn->errorMessage,
+                            appendPQExpBuffer(&conn->errorMessage,
                                               libpq_gettext("out of memory"));
                             pqSaveErrorResult(conn);
                         }
@@ -229,7 +229,7 @@ pqParseInput3(PGconn *conn)
                                                            PGRES_EMPTY_QUERY);
                         if (!conn->result)
                         {
-                            printfPQExpBuffer(&conn->errorMessage,
+                            appendPQExpBuffer(&conn->errorMessage,
                                               libpq_gettext("out of memory"));
                             pqSaveErrorResult(conn);
                         }
@@ -246,7 +246,7 @@ pqParseInput3(PGconn *conn)
                                                                PGRES_COMMAND_OK);
                             if (!conn->result)
                             {
-                                printfPQExpBuffer(&conn->errorMessage,
+                                appendPQExpBuffer(&conn->errorMessage,
                                                   libpq_gettext("out of memory"));
                                 pqSaveErrorResult(conn);
                             }
@@ -326,7 +326,7 @@ pqParseInput3(PGconn *conn)
                                                                PGRES_COMMAND_OK);
                             if (!conn->result)
                             {
-                                printfPQExpBuffer(&conn->errorMessage,
+                                appendPQExpBuffer(&conn->errorMessage,
                                                   libpq_gettext("out of memory"));
                                 pqSaveErrorResult(conn);
                             }
@@ -361,7 +361,7 @@ pqParseInput3(PGconn *conn)
                     else
                     {
                         /* Set up to report error at end of query */
-                        printfPQExpBuffer(&conn->errorMessage,
+                        appendPQExpBuffer(&conn->errorMessage,
                                           libpq_gettext("server sent data (\"D\" message) without prior row
description(\"T\" message)\n")); 
                         pqSaveErrorResult(conn);
                         /* Discard the unexpected message */
@@ -404,7 +404,7 @@ pqParseInput3(PGconn *conn)
                      */
                     break;
                 default:
-                    printfPQExpBuffer(&conn->errorMessage,
+                    appendPQExpBuffer(&conn->errorMessage,
                                       libpq_gettext("unexpected response from server; first received character was
\"%c\"\n"),
                                       id);
                     /* build an error result holding the error message */
@@ -425,7 +425,7 @@ pqParseInput3(PGconn *conn)
         else
         {
             /* Trouble --- report it */
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("message contents do not agree with length in message type \"%c\"\n"),
                               id);
             /* build an error result holding the error message */
@@ -445,7 +445,7 @@ pqParseInput3(PGconn *conn)
 static void
 handleSyncLoss(PGconn *conn, char id, int msgLength)
 {
-    printfPQExpBuffer(&conn->errorMessage,
+    appendPQExpBuffer(&conn->errorMessage,
                       libpq_gettext("lost synchronization with server: got message type \"%c\", length %d\n"),
                       id, msgLength);
     /* build an error result holding the error message */
@@ -621,7 +621,7 @@ advance_and_error:
     if (!errmsg)
         errmsg = libpq_gettext("out of memory for query result");

-    printfPQExpBuffer(&conn->errorMessage, "%s\n", errmsg);
+    appendPQExpBuffer(&conn->errorMessage, "%s\n", errmsg);
     pqSaveErrorResult(conn);

     /*
@@ -721,7 +721,7 @@ advance_and_error:
      */
     if (!errmsg)
         errmsg = libpq_gettext("out of memory");
-    printfPQExpBuffer(&conn->errorMessage, "%s\n", errmsg);
+    appendPQExpBuffer(&conn->errorMessage, "%s\n", errmsg);
     pqSaveErrorResult(conn);

     /*
@@ -848,7 +848,7 @@ set_error_result:
     if (!errmsg)
         errmsg = libpq_gettext("out of memory for query result");

-    printfPQExpBuffer(&conn->errorMessage, "%s\n", errmsg);
+    appendPQExpBuffer(&conn->errorMessage, "%s\n", errmsg);
     pqSaveErrorResult(conn);

     /*
@@ -950,7 +950,7 @@ pqGetErrorNotice3(PGconn *conn, bool isError)
         pqClearAsyncResult(conn);    /* redundant, but be safe */
         conn->result = res;
         if (PQExpBufferDataBroken(workBuf))
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("out of memory"));
         else
             appendPQExpBufferStr(&conn->errorMessage, workBuf.data);
@@ -1695,7 +1695,7 @@ pqGetCopyData3(PGconn *conn, char **buffer, int async)
             *buffer = (char *) malloc(msgLength + 1);
             if (*buffer == NULL)
             {
-                printfPQExpBuffer(&conn->errorMessage,
+                appendPQExpBuffer(&conn->errorMessage,
                                   libpq_gettext("out of memory\n"));
                 return -2;
             }
@@ -1728,7 +1728,7 @@ pqGetline3(PGconn *conn, char *s, int maxlen)
          conn->asyncStatus != PGASYNC_COPY_BOTH) ||
         conn->copy_is_binary)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("PQgetline: not doing text COPY OUT\n"));
         *s = '\0';
         return EOF;
@@ -1834,7 +1834,7 @@ pqEndcopy3(PGconn *conn)
         conn->asyncStatus != PGASYNC_COPY_OUT &&
         conn->asyncStatus != PGASYNC_COPY_BOTH)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("no COPY in progress\n"));
         return 1;
     }
@@ -1868,7 +1868,6 @@ pqEndcopy3(PGconn *conn)

     /* Return to active duty */
     conn->asyncStatus = PGASYNC_BUSY;
-    resetPQExpBuffer(&conn->errorMessage);

     /*
      * Non blocking connections may have to abort at this point.  If everyone
@@ -2091,7 +2090,7 @@ pqFunctionCall3(PGconn *conn, Oid fnid,
                 break;
             default:
                 /* The backend violates the protocol. */
-                printfPQExpBuffer(&conn->errorMessage,
+                appendPQExpBuffer(&conn->errorMessage,
                                   libpq_gettext("protocol error: id=0x%x\n"),
                                   id);
                 pqSaveErrorResult(conn);
diff --git a/src/interfaces/libpq/fe-secure-common.c b/src/interfaces/libpq/fe-secure-common.c
index 45d36359a5..44c69e1d19 100644
--- a/src/interfaces/libpq/fe-secure-common.c
+++ b/src/interfaces/libpq/fe-secure-common.c
@@ -94,7 +94,7 @@ pq_verify_peer_name_matches_certificate_name(PGconn *conn,

     if (!(host && host[0] != '\0'))
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("host name must be specified\n"));
         return -1;
     }
@@ -106,7 +106,7 @@ pq_verify_peer_name_matches_certificate_name(PGconn *conn,
     name = malloc(namelen + 1);
     if (name == NULL)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("out of memory\n"));
         return -1;
     }
@@ -120,7 +120,7 @@ pq_verify_peer_name_matches_certificate_name(PGconn *conn,
     if (namelen != strlen(name))
     {
         free(name);
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("SSL certificate's name contains embedded null\n"));
         return -1;
     }
@@ -167,7 +167,7 @@ pq_verify_peer_name_matches_certificate(PGconn *conn)
     /* Check that we have a hostname to compare with. */
     if (!(host && host[0] != '\0'))
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("host name must be specified for a verified SSL connection\n"));
         return false;
     }
@@ -184,7 +184,7 @@ pq_verify_peer_name_matches_certificate(PGconn *conn)
          */
         if (names_examined > 1)
         {
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_ngettext("server certificate for \"%s\" (and %d other name) does not match host
name\"%s\"\n", 
                                              "server certificate for \"%s\" (and %d other names) does not match host
name\"%s\"\n", 
                                              names_examined - 1),
@@ -192,13 +192,13 @@ pq_verify_peer_name_matches_certificate(PGconn *conn)
         }
         else if (names_examined == 1)
         {
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("server certificate for \"%s\" does not match host name \"%s\"\n"),
                               first_name, host);
         }
         else
         {
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("could not get server's host name from server certificate\n"));
         }
     }
diff --git a/src/interfaces/libpq/fe-secure-gssapi.c b/src/interfaces/libpq/fe-secure-gssapi.c
index 8c0ba69b7c..27e29a4779 100644
--- a/src/interfaces/libpq/fe-secure-gssapi.c
+++ b/src/interfaces/libpq/fe-secure-gssapi.c
@@ -78,7 +78,7 @@
  *
  * On success, returns the number of data bytes consumed (possibly less than
  * len).  On failure, returns -1 with errno set appropriately.  If the errno
- * indicates a non-retryable error, a message is put into conn->errorMessage.
+ * indicates a non-retryable error, a message is added to conn->errorMessage.
  * For retryable errors, caller should call again (passing the same data)
  * once the socket is ready.
  */
@@ -106,7 +106,7 @@ pg_GSS_write(PGconn *conn, const void *ptr, size_t len)
      */
     if (len < PqGSSSendConsumed)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           "GSSAPI caller failed to retransmit all data needing to be retried\n");
         errno = EINVAL;
         return -1;
@@ -205,7 +205,7 @@ pg_GSS_write(PGconn *conn, const void *ptr, size_t len)

         if (conf_state == 0)
         {
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("outgoing GSSAPI message would not use confidentiality\n"));
             errno = EIO;        /* for lack of a better idea */
             goto cleanup;
@@ -213,7 +213,7 @@ pg_GSS_write(PGconn *conn, const void *ptr, size_t len)

         if (output.length > PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32))
         {
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("client tried to send oversize GSSAPI packet (%zu > %zu)\n"),
                               (size_t) output.length,
                               PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32));
@@ -258,7 +258,7 @@ cleanup:
  *
  * Returns the number of data bytes read, or on failure, returns -1
  * with errno set appropriately.  If the errno indicates a non-retryable
- * error, a message is put into conn->errorMessage.  For retryable errors,
+ * error, a message is added to conn->errorMessage.  For retryable errors,
  * caller should call again once the socket is ready.
  */
 ssize_t
@@ -350,7 +350,7 @@ pg_GSS_read(PGconn *conn, void *ptr, size_t len)

         if (input.length > PQ_GSS_RECV_BUFFER_SIZE - sizeof(uint32))
         {
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("oversize GSSAPI packet sent by the server (%zu > %zu)\n"),
                               (size_t) input.length,
                               PQ_GSS_RECV_BUFFER_SIZE - sizeof(uint32));
@@ -399,7 +399,7 @@ pg_GSS_read(PGconn *conn, void *ptr, size_t len)

         if (conf_state == 0)
         {
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("incoming GSSAPI message did not use confidentiality\n"));
             ret = -1;
             errno = EIO;        /* for lack of a better idea */
@@ -500,7 +500,7 @@ pqsecure_open_gss(PGconn *conn)
         PqGSSResultBuffer = malloc(PQ_GSS_RECV_BUFFER_SIZE);
         if (!PqGSSSendBuffer || !PqGSSRecvBuffer || !PqGSSResultBuffer)
         {
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("out of memory\n"));
             return PGRES_POLLING_FAILED;
         }
@@ -578,7 +578,7 @@ pqsecure_open_gss(PGconn *conn)

             PqGSSRecvLength += ret;

-            printfPQExpBuffer(&conn->errorMessage, "%s\n", PqGSSRecvBuffer + 1);
+            appendPQExpBuffer(&conn->errorMessage, "%s\n", PqGSSRecvBuffer + 1);

             return PGRES_POLLING_FAILED;
         }
@@ -592,7 +592,7 @@ pqsecure_open_gss(PGconn *conn)
         input.length = pg_ntoh32(*(uint32 *) PqGSSRecvBuffer);
         if (input.length > PQ_GSS_RECV_BUFFER_SIZE - sizeof(uint32))
         {
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("oversize GSSAPI packet sent by the server (%zu > %zu)\n"),
                               (size_t) input.length,
                               PQ_GSS_RECV_BUFFER_SIZE - sizeof(uint32));
diff --git a/src/interfaces/libpq/fe-secure-openssl.c b/src/interfaces/libpq/fe-secure-openssl.c
index d63e4bb279..539e2053b7 100644
--- a/src/interfaces/libpq/fe-secure-openssl.c
+++ b/src/interfaces/libpq/fe-secure-openssl.c
@@ -181,7 +181,7 @@ rloop:
             if (n < 0)
             {
                 /* Not supposed to happen, so we don't translate the msg */
-                printfPQExpBuffer(&conn->errorMessage,
+                appendPQExpBuffer(&conn->errorMessage,
                                   "SSL_read failed but did not provide error information\n");
                 /* assume the connection is broken */
                 result_errno = ECONNRESET;
@@ -205,19 +205,19 @@ rloop:
                 result_errno = SOCK_ERRNO;
                 if (result_errno == EPIPE ||
                     result_errno == ECONNRESET)
-                    printfPQExpBuffer(&conn->errorMessage,
+                    appendPQExpBuffer(&conn->errorMessage,
                                       libpq_gettext("server closed the connection unexpectedly\n"
                                                     "\tThis probably means the server terminated abnormally\n"
                                                     "\tbefore or while processing the request.\n"));
                 else
-                    printfPQExpBuffer(&conn->errorMessage,
+                    appendPQExpBuffer(&conn->errorMessage,
                                       libpq_gettext("SSL SYSCALL error: %s\n"),
                                       SOCK_STRERROR(result_errno,
                                                     sebuf, sizeof(sebuf)));
             }
             else
             {
-                printfPQExpBuffer(&conn->errorMessage,
+                appendPQExpBuffer(&conn->errorMessage,
                                   libpq_gettext("SSL SYSCALL error: EOF detected\n"));
                 /* assume the connection is broken */
                 result_errno = ECONNRESET;
@@ -228,7 +228,7 @@ rloop:
             {
                 char       *errm = SSLerrmessage(ecode);

-                printfPQExpBuffer(&conn->errorMessage,
+                appendPQExpBuffer(&conn->errorMessage,
                                   libpq_gettext("SSL error: %s\n"), errm);
                 SSLerrfree(errm);
                 /* assume the connection is broken */
@@ -243,13 +243,13 @@ rloop:
              * a clean connection closure, so we should not report it as a
              * server crash.
              */
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("SSL connection has been closed unexpectedly\n"));
             result_errno = ECONNRESET;
             n = -1;
             break;
         default:
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("unrecognized SSL error code: %d\n"),
                               err);
             /* assume the connection is broken */
@@ -290,7 +290,7 @@ pgtls_write(PGconn *conn, const void *ptr, size_t len)
             if (n < 0)
             {
                 /* Not supposed to happen, so we don't translate the msg */
-                printfPQExpBuffer(&conn->errorMessage,
+                appendPQExpBuffer(&conn->errorMessage,
                                   "SSL_write failed but did not provide error information\n");
                 /* assume the connection is broken */
                 result_errno = ECONNRESET;
@@ -312,19 +312,19 @@ pgtls_write(PGconn *conn, const void *ptr, size_t len)
             {
                 result_errno = SOCK_ERRNO;
                 if (result_errno == EPIPE || result_errno == ECONNRESET)
-                    printfPQExpBuffer(&conn->errorMessage,
+                    appendPQExpBuffer(&conn->errorMessage,
                                       libpq_gettext("server closed the connection unexpectedly\n"
                                                     "\tThis probably means the server terminated abnormally\n"
                                                     "\tbefore or while processing the request.\n"));
                 else
-                    printfPQExpBuffer(&conn->errorMessage,
+                    appendPQExpBuffer(&conn->errorMessage,
                                       libpq_gettext("SSL SYSCALL error: %s\n"),
                                       SOCK_STRERROR(result_errno,
                                                     sebuf, sizeof(sebuf)));
             }
             else
             {
-                printfPQExpBuffer(&conn->errorMessage,
+                appendPQExpBuffer(&conn->errorMessage,
                                   libpq_gettext("SSL SYSCALL error: EOF detected\n"));
                 /* assume the connection is broken */
                 result_errno = ECONNRESET;
@@ -335,7 +335,7 @@ pgtls_write(PGconn *conn, const void *ptr, size_t len)
             {
                 char       *errm = SSLerrmessage(ecode);

-                printfPQExpBuffer(&conn->errorMessage,
+                appendPQExpBuffer(&conn->errorMessage,
                                   libpq_gettext("SSL error: %s\n"), errm);
                 SSLerrfree(errm);
                 /* assume the connection is broken */
@@ -350,13 +350,13 @@ pgtls_write(PGconn *conn, const void *ptr, size_t len)
              * a clean connection closure, so we should not report it as a
              * server crash.
              */
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("SSL connection has been closed unexpectedly\n"));
             result_errno = ECONNRESET;
             n = -1;
             break;
         default:
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("unrecognized SSL error code: %d\n"),
                               err);
             /* assume the connection is broken */
@@ -396,7 +396,7 @@ pgtls_get_peer_certificate_hash(PGconn *conn, size_t *len)
     if (!OBJ_find_sigid_algs(X509_get_signature_nid(peer_cert),
                              &algo_nid, NULL))
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("could not determine server certificate signature algorithm\n"));
         return NULL;
     }
@@ -417,7 +417,7 @@ pgtls_get_peer_certificate_hash(PGconn *conn, size_t *len)
             algo_type = EVP_get_digestbynid(algo_nid);
             if (algo_type == NULL)
             {
-                printfPQExpBuffer(&conn->errorMessage,
+                appendPQExpBuffer(&conn->errorMessage,
                                   libpq_gettext("could not find digest for NID %s\n"),
                                   OBJ_nid2sn(algo_nid));
                 return NULL;
@@ -427,7 +427,7 @@ pgtls_get_peer_certificate_hash(PGconn *conn, size_t *len)

     if (!X509_digest(peer_cert, algo_type, hash, &hash_size))
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("could not generate peer certificate hash\n"));
         return NULL;
     }
@@ -436,7 +436,7 @@ pgtls_get_peer_certificate_hash(PGconn *conn, size_t *len)
     cert_hash = malloc(hash_size);
     if (cert_hash == NULL)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("out of memory\n"));
         return NULL;
     }
@@ -484,7 +484,7 @@ openssl_verify_peer_name_matches_certificate_name(PGconn *conn, ASN1_STRING *nam
     /* Should not happen... */
     if (name_entry == NULL)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("SSL certificate's name entry is missing\n"));
         return -1;
     }
@@ -811,7 +811,7 @@ initialize_SSL(PGconn *conn)
     {
         char       *err = SSLerrmessage(ERR_get_error());

-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("could not create SSL context: %s\n"),
                           err);
         SSLerrfree(err);
@@ -850,7 +850,7 @@ initialize_SSL(PGconn *conn)

         if (ssl_min_ver == -1)
         {
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("invalid value \"%s\" for minimum SSL protocol version\n"),
                               conn->ssl_min_protocol_version);
             SSL_CTX_free(SSL_context);
@@ -861,7 +861,7 @@ initialize_SSL(PGconn *conn)
         {
             char       *err = SSLerrmessage(ERR_get_error());

-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("could not set minimum SSL protocol version: %s\n"),
                               err);
             SSLerrfree(err);
@@ -879,7 +879,7 @@ initialize_SSL(PGconn *conn)

         if (ssl_max_ver == -1)
         {
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("invalid value \"%s\" for maximum SSL protocol version\n"),
                               conn->ssl_max_protocol_version);
             SSL_CTX_free(SSL_context);
@@ -890,7 +890,7 @@ initialize_SSL(PGconn *conn)
         {
             char       *err = SSLerrmessage(ERR_get_error());

-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("could not set maximum SSL protocol version: %s\n"),
                               err);
             SSLerrfree(err);
@@ -926,7 +926,7 @@ initialize_SSL(PGconn *conn)
         {
             char       *err = SSLerrmessage(ERR_get_error());

-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("could not read root certificate file \"%s\": %s\n"),
                               fnbuf, err);
             SSLerrfree(err);
@@ -970,11 +970,11 @@ initialize_SSL(PGconn *conn)
              * that it seems worth having a specialized error message for it.
              */
             if (fnbuf[0] == '\0')
-                printfPQExpBuffer(&conn->errorMessage,
+                appendPQExpBuffer(&conn->errorMessage,
                                   libpq_gettext("could not get home directory to locate root certificate file\n"
                                                 "Either provide the file or change sslmode to disable server
certificateverification.\n")); 
             else
-                printfPQExpBuffer(&conn->errorMessage,
+                appendPQExpBuffer(&conn->errorMessage,
                                   libpq_gettext("root certificate file \"%s\" does not exist\n"
                                                 "Either provide the file or change sslmode to disable server
certificateverification.\n"), fnbuf); 
             SSL_CTX_free(SSL_context);
@@ -1005,7 +1005,7 @@ initialize_SSL(PGconn *conn)
          */
         if (errno != ENOENT && errno != ENOTDIR)
         {
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("could not open certificate file \"%s\": %s\n"),
                               fnbuf, strerror_r(errno, sebuf, sizeof(sebuf)));
             SSL_CTX_free(SSL_context);
@@ -1024,7 +1024,7 @@ initialize_SSL(PGconn *conn)
         {
             char       *err = SSLerrmessage(ERR_get_error());

-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("could not read certificate file \"%s\": %s\n"),
                               fnbuf, err);
             SSLerrfree(err);
@@ -1049,7 +1049,7 @@ initialize_SSL(PGconn *conn)
     {
         char       *err = SSLerrmessage(ERR_get_error());

-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("could not establish SSL connection: %s\n"),
                           err);
         SSLerrfree(err);
@@ -1087,7 +1087,7 @@ initialize_SSL(PGconn *conn)

             if (engine_str == NULL)
             {
-                printfPQExpBuffer(&conn->errorMessage,
+                appendPQExpBuffer(&conn->errorMessage,
                                   libpq_gettext("out of memory\n"));
                 return -1;
             }
@@ -1103,7 +1103,7 @@ initialize_SSL(PGconn *conn)
             {
                 char       *err = SSLerrmessage(ERR_get_error());

-                printfPQExpBuffer(&conn->errorMessage,
+                appendPQExpBuffer(&conn->errorMessage,
                                   libpq_gettext("could not load SSL engine \"%s\": %s\n"),
                                   engine_str, err);
                 SSLerrfree(err);
@@ -1115,7 +1115,7 @@ initialize_SSL(PGconn *conn)
             {
                 char       *err = SSLerrmessage(ERR_get_error());

-                printfPQExpBuffer(&conn->errorMessage,
+                appendPQExpBuffer(&conn->errorMessage,
                                   libpq_gettext("could not initialize SSL engine \"%s\": %s\n"),
                                   engine_str, err);
                 SSLerrfree(err);
@@ -1131,7 +1131,7 @@ initialize_SSL(PGconn *conn)
             {
                 char       *err = SSLerrmessage(ERR_get_error());

-                printfPQExpBuffer(&conn->errorMessage,
+                appendPQExpBuffer(&conn->errorMessage,
                                   libpq_gettext("could not read private SSL key \"%s\" from engine \"%s\": %s\n"),
                                   engine_colon, engine_str, err);
                 SSLerrfree(err);
@@ -1145,7 +1145,7 @@ initialize_SSL(PGconn *conn)
             {
                 char       *err = SSLerrmessage(ERR_get_error());

-                printfPQExpBuffer(&conn->errorMessage,
+                appendPQExpBuffer(&conn->errorMessage,
                                   libpq_gettext("could not load private SSL key \"%s\" from engine \"%s\": %s\n"),
                                   engine_colon, engine_str, err);
                 SSLerrfree(err);
@@ -1182,7 +1182,7 @@ initialize_SSL(PGconn *conn)

         if (stat(fnbuf, &buf) != 0)
         {
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("certificate present, but not private key file \"%s\"\n"),
                               fnbuf);
             return -1;
@@ -1190,7 +1190,7 @@ initialize_SSL(PGconn *conn)
 #ifndef WIN32
         if (!S_ISREG(buf.st_mode) || buf.st_mode & (S_IRWXG | S_IRWXO))
         {
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("private key file \"%s\" has group or world access; permissions should be
u=rw(0600) or less\n"), 
                               fnbuf);
             return -1;
@@ -1215,7 +1215,7 @@ initialize_SSL(PGconn *conn)
              */
             if (SSL_use_PrivateKey_file(conn->ssl, fnbuf, SSL_FILETYPE_ASN1) != 1)
             {
-                printfPQExpBuffer(&conn->errorMessage,
+                appendPQExpBuffer(&conn->errorMessage,
                                   libpq_gettext("could not load private key file \"%s\": %s\n"),
                                   fnbuf, err);
                 SSLerrfree(err);
@@ -1233,7 +1233,7 @@ initialize_SSL(PGconn *conn)
     {
         char       *err = SSLerrmessage(ERR_get_error());

-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("certificate does not match private key file \"%s\": %s\n"),
                           fnbuf, err);
         SSLerrfree(err);
@@ -1287,11 +1287,11 @@ open_client_SSL(PGconn *conn)
                     char        sebuf[PG_STRERROR_R_BUFLEN];

                     if (r == -1)
-                        printfPQExpBuffer(&conn->errorMessage,
+                        appendPQExpBuffer(&conn->errorMessage,
                                           libpq_gettext("SSL SYSCALL error: %s\n"),
                                           SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
                     else
-                        printfPQExpBuffer(&conn->errorMessage,
+                        appendPQExpBuffer(&conn->errorMessage,
                                           libpq_gettext("SSL SYSCALL error: EOF detected\n"));
                     pgtls_close(conn);
                     return PGRES_POLLING_FAILED;
@@ -1300,7 +1300,7 @@ open_client_SSL(PGconn *conn)
                 {
                     char       *err = SSLerrmessage(ecode);

-                    printfPQExpBuffer(&conn->errorMessage,
+                    appendPQExpBuffer(&conn->errorMessage,
                                       libpq_gettext("SSL error: %s\n"),
                                       err);
                     SSLerrfree(err);
@@ -1350,7 +1350,7 @@ open_client_SSL(PGconn *conn)
                 }

             default:
-                printfPQExpBuffer(&conn->errorMessage,
+                appendPQExpBuffer(&conn->errorMessage,
                                   libpq_gettext("unrecognized SSL error code: %d\n"),
                                   err);
                 pgtls_close(conn);
@@ -1369,7 +1369,7 @@ open_client_SSL(PGconn *conn)
     {
         char       *err = SSLerrmessage(ERR_get_error());

-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("certificate could not be obtained: %s\n"),
                           err);
         SSLerrfree(err);
diff --git a/src/interfaces/libpq/fe-secure.c b/src/interfaces/libpq/fe-secure.c
index 373c59cb0d..15a37fad30 100644
--- a/src/interfaces/libpq/fe-secure.c
+++ b/src/interfaces/libpq/fe-secure.c
@@ -205,8 +205,8 @@ pqsecure_close(PGconn *conn)
 /*
  *    Read data from a secure connection.
  *
- * On failure, this function is responsible for putting a suitable message
- * into conn->errorMessage.  The caller must still inspect errno, but only
+ * On failure, this function is responsible for appending a suitable message
+ * to conn->errorMessage.  The caller must still inspect errno, but only
  * to determine whether to continue/retry after error.
  */
 ssize_t
@@ -263,14 +263,14 @@ pqsecure_raw_read(PGconn *conn, void *ptr, size_t len)

             case EPIPE:
             case ECONNRESET:
-                printfPQExpBuffer(&conn->errorMessage,
+                appendPQExpBuffer(&conn->errorMessage,
                                   libpq_gettext("server closed the connection unexpectedly\n"
                                                 "\tThis probably means the server terminated abnormally\n"
                                                 "\tbefore or while processing the request.\n"));
                 break;

             default:
-                printfPQExpBuffer(&conn->errorMessage,
+                appendPQExpBuffer(&conn->errorMessage,
                                   libpq_gettext("could not receive data from server: %s\n"),
                                   SOCK_STRERROR(result_errno,
                                                 sebuf, sizeof(sebuf)));
@@ -287,8 +287,8 @@ pqsecure_raw_read(PGconn *conn, void *ptr, size_t len)
 /*
  *    Write data to a secure connection.
  *
- * On failure, this function is responsible for putting a suitable message
- * into conn->errorMessage.  The caller must still inspect errno, but only
+ * On failure, this function is responsible for appending a suitable message
+ * to conn->errorMessage.  The caller must still inspect errno, but only
  * to determine whether to continue/retry after error.
  */
 ssize_t
@@ -376,14 +376,14 @@ retry_masked:
                 /* FALL THRU */

             case ECONNRESET:
-                printfPQExpBuffer(&conn->errorMessage,
+                appendPQExpBuffer(&conn->errorMessage,
                                   libpq_gettext("server closed the connection unexpectedly\n"
                                                 "\tThis probably means the server terminated abnormally\n"
                                                 "\tbefore or while processing the request.\n"));
                 break;

             default:
-                printfPQExpBuffer(&conn->errorMessage,
+                appendPQExpBuffer(&conn->errorMessage,
                                   libpq_gettext("could not send data to server: %s\n"),
                                   SOCK_STRERROR(result_errno,
                                                 sebuf, sizeof(sebuf)));
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index e1018adb9e..4db498369c 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -522,7 +522,11 @@ struct pg_conn
                                  * connection */
 #endif

-    /* Buffer for current error message */
+    /*
+     * Buffer for current error message.  This is cleared at the start of any
+     * connection attempt or query cycle; after that, all code should append
+     * messages to it, never overwrite.
+     */
     PQExpBufferData errorMessage;    /* expansible string */

     /* Buffer for receiving various parts of messages */
@@ -600,7 +604,6 @@ 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 *pqResultAlloc(PGresult *res, size_t nBytes, bool isBinary);
 extern char *pqResultStrdup(PGresult *res, const char *str);
 extern void pqClearAsyncResult(PGconn *conn);
@@ -612,6 +615,7 @@ extern void pqSaveMessageField(PGresult *res, char code,
 extern void pqSaveParameterStatus(PGconn *conn, const char *name,
                                   const char *value);
 extern int    pqRowProcessor(PGconn *conn, const char **errmsgp);
+extern int    PQsendQueryContinue(PGconn *conn, const char *query);

 /* === in fe-protocol2.c === */

@@ -708,7 +712,7 @@ extern void pgtls_init_library(bool do_ssl, int do_crypto);
  * The conn parameter is only used to be able to pass back an error
  * message - no connection-local setup is made here.
  *
- * Returns 0 if OK, -1 on failure (with a message in conn->errorMessage).
+ * Returns 0 if OK, -1 on failure (adding a message to conn->errorMessage).
  */
 extern int    pgtls_init(PGconn *conn);

@@ -725,8 +729,8 @@ extern void pgtls_close(PGconn *conn);
 /*
  *    Read data from a secure connection.
  *
- * On failure, this function is responsible for putting a suitable message
- * into conn->errorMessage.  The caller must still inspect errno, but only
+ * On failure, this function is responsible for appending a suitable message
+ * to conn->errorMessage.  The caller must still inspect errno, but only
  * to determine whether to continue/retry after error.
  */
 extern ssize_t pgtls_read(PGconn *conn, void *ptr, size_t len);
@@ -739,8 +743,8 @@ extern bool pgtls_read_pending(PGconn *conn);
 /*
  *    Write data to a secure connection.
  *
- * On failure, this function is responsible for putting a suitable message
- * into conn->errorMessage.  The caller must still inspect errno, but only
+ * On failure, this function is responsible for appending a suitable message
+ * to conn->errorMessage.  The caller must still inspect errno, but only
  * to determine whether to continue/retry after error.
  */
 extern ssize_t pgtls_write(PGconn *conn, const void *ptr, size_t len);
diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl
index 11dc98ee0a..2b501166b8 100644
--- a/src/bin/pg_dump/t/002_pg_dump.pl
+++ b/src/bin/pg_dump/t/002_pg_dump.pl
@@ -3460,7 +3460,7 @@ foreach my $db (sort keys %create_sql)

 command_fails_like(
     [ 'pg_dump', '-p', "$port", 'qqq' ],
-    qr/\Qpg_dump: error: connection to database "qqq" failed: FATAL:  database "qqq" does not exist\E/,
+    qr/pg_dump: error: connection to database "qqq" failed: could not connect to .*: FATAL:  database "qqq" does not
exist/,
     'connecting to a non-existent database');

 #########################################
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 84e2868104..5b0770d8cc 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -1669,15 +1669,17 @@ getHostaddr(PGconn *conn, char *host_addr, int host_addr_len)
 }

 /* ----------
- * connectFailureMessage -
- * create a friendly error message on connection failure.
+ * emitCouldNotConnect -
+ * Speculatively append "could not connect to ...: " to conn->errorMessage
+ * once we've identified the current connection target address.  This ensures
+ * that any subsequent error message will be properly attributed to the
+ * server we couldn't connect to.  conn->raddr must be valid, and the result
+ * of getHostaddr() must be supplied.
  * ----------
  */
 static void
-connectFailureMessage(PGconn *conn, int errorno)
+emitCouldNotConnect(PGconn *conn, const char *host_addr)
 {
-    char        sebuf[PG_STRERROR_R_BUFLEN];
-
 #ifdef HAVE_UNIX_SOCKETS
     if (IS_AF_UNIX(conn->raddr.addr.ss_family))
     {
@@ -1688,25 +1690,15 @@ connectFailureMessage(PGconn *conn, int errorno)
                            service, sizeof(service),
                            NI_NUMERICSERV);
         appendPQExpBuffer(&conn->errorMessage,
-                          libpq_gettext("could not connect to server: %s\n"
-                                        "\tIs the server running locally and accepting\n"
-                                        "\tconnections on Unix domain socket \"%s\"?\n"),
-                          SOCK_STRERROR(errorno, sebuf, sizeof(sebuf)),
+                          libpq_gettext("could not connect to socket \"%s\": "),
                           service);
     }
     else
 #endif                            /* HAVE_UNIX_SOCKETS */
     {
-        char        host_addr[NI_MAXHOST];
         const char *displayed_host;
         const char *displayed_port;

-        /*
-         * Optionally display the network address with the hostname. This is
-         * useful to distinguish between IPv4 and IPv6 connections.
-         */
-        getHostaddr(conn, host_addr, NI_MAXHOST);
-
         /* To which host and port were we actually connecting? */
         if (conn->connhost[conn->whichhost].type == CHT_HOST_ADDRESS)
             displayed_host = conn->connhost[conn->whichhost].hostaddr;
@@ -1722,26 +1714,46 @@ connectFailureMessage(PGconn *conn, int errorno)
          * looked-up IP address.
          */
         if (conn->connhost[conn->whichhost].type != CHT_HOST_ADDRESS &&
-            strlen(host_addr) > 0 &&
+            host_addr[0] &&
             strcmp(displayed_host, host_addr) != 0)
             appendPQExpBuffer(&conn->errorMessage,
-                              libpq_gettext("could not connect to server: %s\n"
-                                            "\tIs the server running on host \"%s\" (%s) and accepting\n"
-                                            "\tTCP/IP connections on port %s?\n"),
-                              SOCK_STRERROR(errorno, sebuf, sizeof(sebuf)),
+                              libpq_gettext("could not connect to host \"%s\" (%s), port %s: "),
                               displayed_host, host_addr,
                               displayed_port);
         else
             appendPQExpBuffer(&conn->errorMessage,
-                              libpq_gettext("could not connect to server: %s\n"
-                                            "\tIs the server running on host \"%s\" and accepting\n"
-                                            "\tTCP/IP connections on port %s?\n"),
-                              SOCK_STRERROR(errorno, sebuf, sizeof(sebuf)),
+                              libpq_gettext("could not connect to host \"%s\", port %s: "),
                               displayed_host,
                               displayed_port);
     }
 }

+/* ----------
+ * connectFailureMessage -
+ * create a friendly error message on connection failure,
+ * using the given errno value.  Use this for error cases that
+ * imply that there's no server there.
+ * ----------
+ */
+static void
+connectFailureMessage(PGconn *conn, int errorno)
+{
+    char        sebuf[PG_STRERROR_R_BUFLEN];
+
+    appendPQExpBuffer(&conn->errorMessage,
+                      "%s\n",
+                      SOCK_STRERROR(errorno, sebuf, sizeof(sebuf)));
+
+#ifdef HAVE_UNIX_SOCKETS
+    if (IS_AF_UNIX(conn->raddr.addr.ss_family))
+        appendPQExpBufferStr(&conn->errorMessage,
+                             libpq_gettext("\tIs the server running locally and accepting connections on that
socket?\n"));
+    else
+#endif
+        appendPQExpBufferStr(&conn->errorMessage,
+                             libpq_gettext("\tIs the server running on that host and accepting TCP/IP
connections?\n"));
+}
+
 /*
  * Should we use keepalives?  Returns 1 if yes, 0 if no, and -1 if
  * conn->keepalives is set to a value which is not parseable as an
@@ -2476,30 +2488,30 @@ keep_going:                        /* We will come back to here until there is
                         goto keep_going;
                     }

-                    /* Remember current address for possible error msg */
+                    /* Remember current address for possible use later */
                     memcpy(&conn->raddr.addr, addr_cur->ai_addr,
                            addr_cur->ai_addrlen);
                     conn->raddr.salen = addr_cur->ai_addrlen;

-                    /* set connip */
+                    /*
+                     * Set connip, too.  Note we purposely ignore strdup
+                     * failure; not a big problem if it fails.
+                     */
                     if (conn->connip != NULL)
                     {
                         free(conn->connip);
                         conn->connip = NULL;
                     }
-
                     getHostaddr(conn, host_addr, NI_MAXHOST);
-                    if (strlen(host_addr) > 0)
+                    if (host_addr[0])
                         conn->connip = strdup(host_addr);

-                    /*
-                     * purposely ignore strdup failure; not a big problem if
-                     * it fails anyway.
-                     */
-
+                    /* Try to create the socket */
                     conn->sock = socket(addr_cur->ai_family, SOCK_STREAM, 0);
                     if (conn->sock == PGINVALID_SOCKET)
                     {
+                        int            errorno = SOCK_ERRNO;
+
                         /*
                          * Silently ignore socket() failure if we have more
                          * addresses to try; this reduces useless chatter in
@@ -2512,12 +2524,20 @@ keep_going:                        /* We will come back to here until there is
                             conn->try_next_addr = true;
                             goto keep_going;
                         }
+                        emitCouldNotConnect(conn, host_addr);
                         appendPQExpBuffer(&conn->errorMessage,
                                           libpq_gettext("could not create socket: %s\n"),
-                                          SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
+                                          SOCK_STRERROR(errorno, sebuf, sizeof(sebuf)));
                         goto error_return;
                     }

+                    /*
+                     * Once we've identified a target address, all errors
+                     * except the preceding socket()-failure case should be
+                     * prefixed with "could not connect to <target>: ".
+                     */
+                    emitCouldNotConnect(conn, host_addr);
+
                     /*
                      * Select socket options: no delay of outgoing data for
                      * TCP sockets, nonblock mode, close-on-exec.  Try the
@@ -3608,9 +3628,6 @@ keep_going:                        /* We will come back to here until there is
             }
         case CONNECTION_CHECK_WRITABLE:
             {
-                const char *displayed_host;
-                const char *displayed_port;
-
                 conn->status = CONNECTION_OK;
                 if (!PQconsumeInput(conn))
                     goto error_return;
@@ -3634,19 +3651,8 @@ keep_going:                        /* We will come back to here until there is
                         PQclear(res);

                         /* Append error report to conn->errorMessage. */
-                        if (conn->connhost[conn->whichhost].type == CHT_HOST_ADDRESS)
-                            displayed_host = conn->connhost[conn->whichhost].hostaddr;
-                        else
-                            displayed_host = conn->connhost[conn->whichhost].host;
-                        displayed_port = conn->connhost[conn->whichhost].port;
-                        if (displayed_port == NULL || displayed_port[0] == '\0')
-                            displayed_port = DEF_PGPORT_STR;
-
-                        appendPQExpBuffer(&conn->errorMessage,
-                                          libpq_gettext("could not make a writable "
-                                                        "connection to server "
-                                                        "\"%s:%s\"\n"),
-                                          displayed_host, displayed_port);
+                        appendPQExpBufferStr(&conn->errorMessage,
+                                             libpq_gettext("session is read-only\n"));

                         /* Close connection politely. */
                         conn->status = CONNECTION_OK;
@@ -3679,17 +3685,8 @@ keep_going:                        /* We will come back to here until there is
                     PQclear(res);

                 /* Append error report to conn->errorMessage. */
-                if (conn->connhost[conn->whichhost].type == CHT_HOST_ADDRESS)
-                    displayed_host = conn->connhost[conn->whichhost].hostaddr;
-                else
-                    displayed_host = conn->connhost[conn->whichhost].host;
-                displayed_port = conn->connhost[conn->whichhost].port;
-                if (displayed_port == NULL || displayed_port[0] == '\0')
-                    displayed_port = DEF_PGPORT_STR;
-                appendPQExpBuffer(&conn->errorMessage,
-                                  libpq_gettext("test \"SHOW transaction_read_only\" failed "
-                                                "on server \"%s:%s\"\n"),
-                                  displayed_host, displayed_port);
+                appendPQExpBufferStr(&conn->errorMessage,
+                                     libpq_gettext("test \"SHOW transaction_read_only\" failed\n"));

                 /* Close connection politely. */
                 conn->status = CONNECTION_OK;
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 5b0770d8cc..8391e2c46a 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -3315,6 +3315,20 @@ keep_going:                        /* We will come back to here until there is
                     /* OK, we read the message; mark data consumed */
                     conn->inStart = conn->inCursor;

+                    /*
+                     * If error is "cannot connect now", try the next host if
+                     * any (but we don't want to consider additional addresses
+                     * for this host, nor is there much point in changing SSL
+                     * or GSS mode).  This is helpful when dealing with
+                     * standby servers that might not be in hot-standby state.
+                     */
+                    if (strcmp(conn->last_sqlstate,
+                               ERRCODE_CANNOT_CONNECT_NOW) == 0)
+                    {
+                        conn->try_next_host = true;
+                        goto keep_going;
+                    }
+
                     /* Check to see if we should mention pgpassfile */
                     pgpassfileWarning(conn);


I wrote:
> I feel pretty good about 0001: it might be committable as-is.  0002 is
> probably subject to bikeshedding, plus it has a problem in the ECPG tests.
> Two of the error messages are now unstable because they expose
> chosen-at-random socket paths:
> ...
> I don't have any non-hacky ideas what to do about that.  The extra detail
> seems useful to end users, but we don't have any infrastructure that
> would allow filtering it out in the ECPG tests.

So far the only solution that comes to mind is to introduce some
infrastructure to do that filtering.  0001-0003 below are unchanged,
0004 patches up the ecpg test framework with a rather ad-hoc filtering
function.  I'd feel worse about this if there weren't already a very
ad-hoc filtering function there ;-)

This set passes check-world for me; we'll soon see what the cfbot
thinks.

            regards, tom lane

diff --git a/src/interfaces/libpq/fe-auth-scram.c b/src/interfaces/libpq/fe-auth-scram.c
index b76f0befd0..8b60378379 100644
--- a/src/interfaces/libpq/fe-auth-scram.c
+++ b/src/interfaces/libpq/fe-auth-scram.c
@@ -208,13 +208,13 @@ pg_fe_scram_exchange(void *opaq, char *input, int inputlen,
     {
         if (inputlen == 0)
         {
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("malformed SCRAM message (empty message)\n"));
             goto error;
         }
         if (inputlen != strlen(input))
         {
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("malformed SCRAM message (length mismatch)\n"));
             goto error;
         }
@@ -258,14 +258,14 @@ pg_fe_scram_exchange(void *opaq, char *input, int inputlen,
              */
             if (!verify_server_signature(state, success))
             {
-                printfPQExpBuffer(&conn->errorMessage,
+                appendPQExpBuffer(&conn->errorMessage,
                                   libpq_gettext("could not verify server signature\n"));
                 goto error;
             }

             if (!*success)
             {
-                printfPQExpBuffer(&conn->errorMessage,
+                appendPQExpBuffer(&conn->errorMessage,
                                   libpq_gettext("incorrect server signature\n"));
             }
             *done = true;
@@ -274,7 +274,7 @@ pg_fe_scram_exchange(void *opaq, char *input, int inputlen,

         default:
             /* shouldn't happen */
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("invalid SCRAM exchange state\n"));
             goto error;
     }
@@ -287,6 +287,11 @@ error:

 /*
  * Read value for an attribute part of a SCRAM message.
+ *
+ * The buffer at **input is destructively modified, and *input is
+ * advanced over the "attr=value" string and any following comma.
+ *
+ * On failure, append an error message to *errorMessage and return NULL.
  */
 static char *
 read_attr_value(char **input, char attr, PQExpBuffer errorMessage)
@@ -296,7 +301,7 @@ read_attr_value(char **input, char attr, PQExpBuffer errorMessage)

     if (*begin != attr)
     {
-        printfPQExpBuffer(errorMessage,
+        appendPQExpBuffer(errorMessage,
                           libpq_gettext("malformed SCRAM message (attribute \"%c\" expected)\n"),
                           attr);
         return NULL;
@@ -305,7 +310,7 @@ read_attr_value(char **input, char attr, PQExpBuffer errorMessage)

     if (*begin != '=')
     {
-        printfPQExpBuffer(errorMessage,
+        appendPQExpBuffer(errorMessage,
                           libpq_gettext("malformed SCRAM message (expected character \"=\" for attribute \"%c\")\n"),
                           attr);
         return NULL;
@@ -346,7 +351,7 @@ build_client_first_message(fe_scram_state *state)
      */
     if (!pg_strong_random(raw_nonce, SCRAM_RAW_NONCE_LEN))
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("could not generate nonce\n"));
         return NULL;
     }
@@ -356,7 +361,7 @@ build_client_first_message(fe_scram_state *state)
     state->client_nonce = malloc(encoded_len + 1);
     if (state->client_nonce == NULL)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("out of memory\n"));
         return NULL;
     }
@@ -364,7 +369,7 @@ build_client_first_message(fe_scram_state *state)
                                 state->client_nonce, encoded_len);
     if (encoded_len < 0)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("could not encode nonce\n"));
         return NULL;
     }
@@ -431,7 +436,7 @@ build_client_first_message(fe_scram_state *state)

 oom_error:
     termPQExpBuffer(&buf);
-    printfPQExpBuffer(&conn->errorMessage,
+    appendPQExpBuffer(&conn->errorMessage,
                       libpq_gettext("out of memory\n"));
     return NULL;
 }
@@ -508,7 +513,7 @@ build_client_final_message(fe_scram_state *state)
             free(cbind_data);
             free(cbind_input);
             termPQExpBuffer(&buf);
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               "could not encode cbind data for channel binding\n");
             return NULL;
         }
@@ -523,7 +528,7 @@ build_client_final_message(fe_scram_state *state)
          * Shouldn't happen.
          */
         termPQExpBuffer(&buf);
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           "channel binding not supported by this build\n");
         return NULL;
 #endif                            /* HAVE_PGTLS_GET_PEER_CERTIFICATE_HASH */
@@ -553,7 +558,7 @@ build_client_final_message(fe_scram_state *state)
                                 client_proof))
     {
         termPQExpBuffer(&buf);
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("could not calculate client proof\n"));
         return NULL;
     }
@@ -569,7 +574,7 @@ build_client_final_message(fe_scram_state *state)
     if (encoded_len < 0)
     {
         termPQExpBuffer(&buf);
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("could not encode client proof\n"));
         return NULL;
     }
@@ -585,7 +590,7 @@ build_client_final_message(fe_scram_state *state)

 oom_error:
     termPQExpBuffer(&buf);
-    printfPQExpBuffer(&conn->errorMessage,
+    appendPQExpBuffer(&conn->errorMessage,
                       libpq_gettext("out of memory\n"));
     return NULL;
 }
@@ -606,7 +611,7 @@ read_server_first_message(fe_scram_state *state, char *input)
     state->server_first_message = strdup(input);
     if (state->server_first_message == NULL)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("out of memory\n"));
         return false;
     }
@@ -616,7 +621,7 @@ read_server_first_message(fe_scram_state *state, char *input)
                             &conn->errorMessage);
     if (nonce == NULL)
     {
-        /* read_attr_value() has generated an error string */
+        /* read_attr_value() has appended an error string */
         return false;
     }

@@ -624,7 +629,7 @@ read_server_first_message(fe_scram_state *state, char *input)
     if (strlen(nonce) < strlen(state->client_nonce) ||
         memcmp(nonce, state->client_nonce, strlen(state->client_nonce)) != 0)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("invalid SCRAM response (nonce mismatch)\n"));
         return false;
     }
@@ -632,7 +637,7 @@ read_server_first_message(fe_scram_state *state, char *input)
     state->nonce = strdup(nonce);
     if (state->nonce == NULL)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("out of memory\n"));
         return false;
     }
@@ -640,14 +645,14 @@ read_server_first_message(fe_scram_state *state, char *input)
     encoded_salt = read_attr_value(&input, 's', &conn->errorMessage);
     if (encoded_salt == NULL)
     {
-        /* read_attr_value() has generated an error string */
+        /* read_attr_value() has appended an error string */
         return false;
     }
     decoded_salt_len = pg_b64_dec_len(strlen(encoded_salt));
     state->salt = malloc(decoded_salt_len);
     if (state->salt == NULL)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("out of memory\n"));
         return false;
     }
@@ -657,7 +662,7 @@ read_server_first_message(fe_scram_state *state, char *input)
                                    decoded_salt_len);
     if (state->saltlen < 0)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("malformed SCRAM message (invalid salt)\n"));
         return false;
     }
@@ -665,19 +670,19 @@ read_server_first_message(fe_scram_state *state, char *input)
     iterations_str = read_attr_value(&input, 'i', &conn->errorMessage);
     if (iterations_str == NULL)
     {
-        /* read_attr_value() has generated an error string */
+        /* read_attr_value() has appended an error string */
         return false;
     }
     state->iterations = strtol(iterations_str, &endptr, 10);
     if (*endptr != '\0' || state->iterations < 1)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("malformed SCRAM message (invalid iteration count)\n"));
         return false;
     }

     if (*input != '\0')
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("malformed SCRAM message (garbage at end of server-first-message)\n"));

     return true;
@@ -697,7 +702,7 @@ read_server_final_message(fe_scram_state *state, char *input)
     state->server_final_message = strdup(input);
     if (!state->server_final_message)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("out of memory\n"));
         return false;
     }
@@ -708,7 +713,12 @@ read_server_final_message(fe_scram_state *state, char *input)
         char       *errmsg = read_attr_value(&input, 'e',
                                              &conn->errorMessage);

-        printfPQExpBuffer(&conn->errorMessage,
+        if (errmsg == NULL)
+        {
+            /* read_attr_value() has appended an error message */
+            return false;
+        }
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("error received from server in SCRAM exchange: %s\n"),
                           errmsg);
         return false;
@@ -719,19 +729,19 @@ read_server_final_message(fe_scram_state *state, char *input)
                                                &conn->errorMessage);
     if (encoded_server_signature == NULL)
     {
-        /* read_attr_value() has generated an error message */
+        /* read_attr_value() has appended an error message */
         return false;
     }

     if (*input != '\0')
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("malformed SCRAM message (garbage at end of server-final-message)\n"));

     server_signature_len = pg_b64_dec_len(strlen(encoded_server_signature));
     decoded_server_signature = malloc(server_signature_len);
     if (!decoded_server_signature)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("out of memory\n"));
         return false;
     }
@@ -743,7 +753,7 @@ read_server_final_message(fe_scram_state *state, char *input)
     if (server_signature_len != SCRAM_KEY_LEN)
     {
         free(decoded_server_signature);
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("malformed SCRAM message (invalid server signature)\n"));
         return false;
     }
diff --git a/src/interfaces/libpq/fe-auth.c b/src/interfaces/libpq/fe-auth.c
index a25fe4dd17..089226dc8a 100644
--- a/src/interfaces/libpq/fe-auth.c
+++ b/src/interfaces/libpq/fe-auth.c
@@ -72,7 +72,7 @@ pg_GSS_continue(PGconn *conn, int payloadlen)
         ginbuf.value = malloc(payloadlen);
         if (!ginbuf.value)
         {
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("out of memory allocating GSSAPI buffer (%d)\n"),
                               payloadlen);
             return STATUS_ERROR;
@@ -154,14 +154,14 @@ pg_GSS_startup(PGconn *conn, int payloadlen)

     if (!(host && host[0] != '\0'))
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("host name must be specified\n"));
         return STATUS_ERROR;
     }

     if (conn->gctx)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("duplicate GSS authentication request\n"));
         return STATUS_ERROR;
     }
@@ -195,10 +195,10 @@ pg_SSPI_error(PGconn *conn, const char *mprefix, SECURITY_STATUS r)
                       FORMAT_MESSAGE_FROM_SYSTEM,
                       NULL, r, 0,
                       sysmsg, sizeof(sysmsg), NULL) == 0)
-        printfPQExpBuffer(&conn->errorMessage, "%s: SSPI error %x\n",
+        appendPQExpBuffer(&conn->errorMessage, "%s: SSPI error %x\n",
                           mprefix, (unsigned int) r);
     else
-        printfPQExpBuffer(&conn->errorMessage, "%s: %s (%x)\n",
+        appendPQExpBuffer(&conn->errorMessage, "%s: %s (%x)\n",
                           mprefix, sysmsg, (unsigned int) r);
 }

@@ -226,7 +226,7 @@ pg_SSPI_continue(PGconn *conn, int payloadlen)
         inputbuf = malloc(payloadlen);
         if (!inputbuf)
         {
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("out of memory allocating SSPI buffer (%d)\n"),
                               payloadlen);
             return STATUS_ERROR;
@@ -286,7 +286,7 @@ pg_SSPI_continue(PGconn *conn, int payloadlen)
         conn->sspictx = malloc(sizeof(CtxtHandle));
         if (conn->sspictx == NULL)
         {
-            printfPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory\n"));
+            appendPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory\n"));
             return STATUS_ERROR;
         }
         memcpy(conn->sspictx, &newContext, sizeof(CtxtHandle));
@@ -305,7 +305,7 @@ pg_SSPI_continue(PGconn *conn, int payloadlen)
              * authentication. Keep check in case it shows up with other
              * authentication methods later.
              */
-            printfPQExpBuffer(&conn->errorMessage, "SSPI returned invalid number of output buffers\n");
+            appendPQExpBuffer(&conn->errorMessage, "SSPI returned invalid number of output buffers\n");
             return STATUS_ERROR;
         }

@@ -345,7 +345,7 @@ pg_SSPI_startup(PGconn *conn, int use_negotiate, int payloadlen)

     if (conn->sspictx)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("duplicate SSPI authentication request\n"));
         return STATUS_ERROR;
     }
@@ -356,7 +356,7 @@ pg_SSPI_startup(PGconn *conn, int use_negotiate, int payloadlen)
     conn->sspicred = malloc(sizeof(CredHandle));
     if (conn->sspicred == NULL)
     {
-        printfPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory\n"));
+        appendPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory\n"));
         return STATUS_ERROR;
     }

@@ -384,14 +384,14 @@ pg_SSPI_startup(PGconn *conn, int use_negotiate, int payloadlen)
      */
     if (!(host && host[0] != '\0'))
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("host name must be specified\n"));
         return STATUS_ERROR;
     }
     conn->sspitarget = malloc(strlen(conn->krbsrvname) + strlen(host) + 2);
     if (!conn->sspitarget)
     {
-        printfPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory\n"));
+        appendPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory\n"));
         return STATUS_ERROR;
     }
     sprintf(conn->sspitarget, "%s/%s", conn->krbsrvname, host);
@@ -425,14 +425,14 @@ pg_SASL_init(PGconn *conn, int payloadlen)
     if (conn->channel_binding[0] == 'r' &&    /* require */
         !conn->ssl_in_use)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("channel binding required, but SSL not in use\n"));
         goto error;
     }

     if (conn->sasl_state)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("duplicate SASL authentication request\n"));
         goto error;
     }
@@ -448,7 +448,7 @@ pg_SASL_init(PGconn *conn, int payloadlen)
     {
         if (pqGets(&mechanism_buf, conn))
         {
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               "fe_sendauth: invalid authentication request from server: invalid list of authentication
mechanisms\n");
             goto error;
         }
@@ -488,7 +488,7 @@ pg_SASL_init(PGconn *conn, int payloadlen)
                  */
                 if (conn->channel_binding[0] == 'r')    /* require */
                 {
-                    printfPQExpBuffer(&conn->errorMessage,
+                    appendPQExpBuffer(&conn->errorMessage,
                                       libpq_gettext("channel binding is required, but client does not support it\n"));
                     goto error;
                 }
@@ -505,7 +505,7 @@ pg_SASL_init(PGconn *conn, int payloadlen)
                  * the client and server supported it. The SCRAM exchange
                  * checks for that, to prevent downgrade attacks.
                  */
-                printfPQExpBuffer(&conn->errorMessage,
+                appendPQExpBuffer(&conn->errorMessage,
                                   libpq_gettext("server offered SCRAM-SHA-256-PLUS authentication over a non-SSL
connection\n"));
                 goto error;
             }
@@ -517,7 +517,7 @@ pg_SASL_init(PGconn *conn, int payloadlen)

     if (!selected_mechanism)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("none of the server's SASL authentication mechanisms are supported\n"));
         goto error;
     }
@@ -525,7 +525,7 @@ pg_SASL_init(PGconn *conn, int payloadlen)
     if (conn->channel_binding[0] == 'r' &&    /* require */
         strcmp(selected_mechanism, SCRAM_SHA_256_PLUS_NAME) != 0)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("channel binding is required, but server did not offer an authentication
methodthat supports channel binding\n")); 
         goto error;
     }
@@ -546,7 +546,7 @@ pg_SASL_init(PGconn *conn, int payloadlen)
         password = conn->pgpass;
     if (password == NULL || password[0] == '\0')
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           PQnoPasswordSupplied);
         goto error;
     }
@@ -607,7 +607,7 @@ oom_error:
     termPQExpBuffer(&mechanism_buf);
     if (initialresponse)
         free(initialresponse);
-    printfPQExpBuffer(&conn->errorMessage,
+    appendPQExpBuffer(&conn->errorMessage,
                       libpq_gettext("out of memory\n"));
     return STATUS_ERROR;
 }
@@ -631,7 +631,7 @@ pg_SASL_continue(PGconn *conn, int payloadlen, bool final)
     challenge = malloc(payloadlen + 1);
     if (!challenge)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("out of memory allocating SASL buffer (%d)\n"),
                           payloadlen);
         return STATUS_ERROR;
@@ -656,7 +656,7 @@ pg_SASL_continue(PGconn *conn, int payloadlen, bool final)
         if (outputlen != 0)
             free(output);

-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("AuthenticationSASLFinal received from server, but SASL authentication was not
completed\n"));
         return STATUS_ERROR;
     }
@@ -726,14 +726,14 @@ pg_local_sendauth(PGconn *conn)
     {
         char        sebuf[PG_STRERROR_R_BUFLEN];

-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           "pg_local_sendauth: sendmsg: %s\n",
                           strerror_r(errno, sebuf, sizeof(sebuf)));
         return STATUS_ERROR;
     }
     return STATUS_OK;
 #else
-    printfPQExpBuffer(&conn->errorMessage,
+    appendPQExpBuffer(&conn->errorMessage,
                       libpq_gettext("SCM_CRED authentication method not supported\n"));
     return STATUS_ERROR;
 #endif
@@ -766,7 +766,7 @@ pg_password_sendauth(PGconn *conn, const char *password, AuthRequest areq)
                 crypt_pwd = malloc(2 * (MD5_PASSWD_LEN + 1));
                 if (!crypt_pwd)
                 {
-                    printfPQExpBuffer(&conn->errorMessage,
+                    appendPQExpBuffer(&conn->errorMessage,
                                       libpq_gettext("out of memory\n"));
                     return STATUS_ERROR;
                 }
@@ -832,13 +832,13 @@ check_expected_areq(AuthRequest areq, PGconn *conn)
             case AUTH_REQ_OK:
                 if (!pg_fe_scram_channel_bound(conn->sasl_state))
                 {
-                    printfPQExpBuffer(&conn->errorMessage,
+                    appendPQExpBuffer(&conn->errorMessage,
                                       libpq_gettext("channel binding required, but server authenticated client without
channelbinding\n")); 
                     result = false;
                 }
                 break;
             default:
-                printfPQExpBuffer(&conn->errorMessage,
+                appendPQExpBuffer(&conn->errorMessage,
                                   libpq_gettext("channel binding required but not supported by server's authentication
request\n"));
                 result = false;
                 break;
@@ -862,6 +862,8 @@ check_expected_areq(AuthRequest areq, PGconn *conn)
 int
 pg_fe_sendauth(AuthRequest areq, int payloadlen, PGconn *conn)
 {
+    int            oldmsglen;
+
     if (!check_expected_areq(areq, conn))
         return STATUS_ERROR;

@@ -871,12 +873,12 @@ pg_fe_sendauth(AuthRequest areq, int payloadlen, PGconn *conn)
             break;

         case AUTH_REQ_KRB4:
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("Kerberos 4 authentication not supported\n"));
             return STATUS_ERROR;

         case AUTH_REQ_KRB5:
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("Kerberos 5 authentication not supported\n"));
             return STATUS_ERROR;

@@ -947,7 +949,7 @@ pg_fe_sendauth(AuthRequest areq, int payloadlen, PGconn *conn)
             /* No GSSAPI *or* SSPI support */
         case AUTH_REQ_GSS:
         case AUTH_REQ_GSS_CONT:
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("GSSAPI authentication not supported\n"));
             return STATUS_ERROR;
 #endif                            /* defined(ENABLE_GSS) || defined(ENABLE_SSPI) */
@@ -979,7 +981,7 @@ pg_fe_sendauth(AuthRequest areq, int payloadlen, PGconn *conn)
              */
 #if !defined(ENABLE_GSS)
         case AUTH_REQ_SSPI:
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("SSPI authentication not supported\n"));
             return STATUS_ERROR;
 #endif                            /* !define(ENABLE_GSS) */
@@ -987,7 +989,7 @@ pg_fe_sendauth(AuthRequest areq, int payloadlen, PGconn *conn)


         case AUTH_REQ_CRYPT:
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("Crypt authentication not supported\n"));
             return STATUS_ERROR;

@@ -1002,13 +1004,13 @@ pg_fe_sendauth(AuthRequest areq, int payloadlen, PGconn *conn)
                     password = conn->pgpass;
                 if (password == NULL || password[0] == '\0')
                 {
-                    printfPQExpBuffer(&conn->errorMessage,
+                    appendPQExpBuffer(&conn->errorMessage,
                                       PQnoPasswordSupplied);
                     return STATUS_ERROR;
                 }
                 if (pg_password_sendauth(conn, password, areq) != STATUS_OK)
                 {
-                    printfPQExpBuffer(&conn->errorMessage,
+                    appendPQExpBuffer(&conn->errorMessage,
                                       "fe_sendauth: error sending password authentication\n");
                     return STATUS_ERROR;
                 }
@@ -1032,16 +1034,17 @@ pg_fe_sendauth(AuthRequest areq, int payloadlen, PGconn *conn)
         case AUTH_REQ_SASL_FIN:
             if (conn->sasl_state == NULL)
             {
-                printfPQExpBuffer(&conn->errorMessage,
+                appendPQExpBuffer(&conn->errorMessage,
                                   "fe_sendauth: invalid authentication request from server: AUTH_REQ_SASL_CONT without
AUTH_REQ_SASL\n");
                 return STATUS_ERROR;
             }
+            oldmsglen = conn->errorMessage.len;
             if (pg_SASL_continue(conn, payloadlen,
                                  (areq == AUTH_REQ_SASL_FIN)) != STATUS_OK)
             {
-                /* Use error message, if set already */
-                if (conn->errorMessage.len == 0)
-                    printfPQExpBuffer(&conn->errorMessage,
+                /* Use this message if pg_SASL_continue didn't supply one */
+                if (conn->errorMessage.len == oldmsglen)
+                    appendPQExpBuffer(&conn->errorMessage,
                                       "fe_sendauth: error in SASL authentication\n");
                 return STATUS_ERROR;
             }
@@ -1053,7 +1056,7 @@ pg_fe_sendauth(AuthRequest areq, int payloadlen, PGconn *conn)
             break;

         default:
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("authentication method %u not supported\n"), areq);
             return STATUS_ERROR;
     }
@@ -1067,7 +1070,7 @@ pg_fe_sendauth(AuthRequest areq, int payloadlen, PGconn *conn)
  *
  * Returns a pointer to malloc'd space containing whatever name the user
  * has authenticated to the system.  If there is an error, return NULL,
- * and put a suitable error message in *errorMessage if that's not NULL.
+ * and append a suitable error message to *errorMessage if that's not NULL.
  */
 char *
 pg_fe_getauthname(PQExpBuffer errorMessage)
@@ -1100,7 +1103,7 @@ pg_fe_getauthname(PQExpBuffer errorMessage)
     if (GetUserName(username, &namesize))
         name = username;
     else if (errorMessage)
-        printfPQExpBuffer(errorMessage,
+        appendPQExpBuffer(errorMessage,
                           libpq_gettext("user name lookup failure: error code %lu\n"),
                           GetLastError());
 #else
@@ -1110,12 +1113,12 @@ pg_fe_getauthname(PQExpBuffer errorMessage)
     else if (errorMessage)
     {
         if (pwerr != 0)
-            printfPQExpBuffer(errorMessage,
+            appendPQExpBuffer(errorMessage,
                               libpq_gettext("could not look up local user ID %d: %s\n"),
                               (int) user_id,
                               strerror_r(pwerr, pwdbuf, sizeof(pwdbuf)));
         else
-            printfPQExpBuffer(errorMessage,
+            appendPQExpBuffer(errorMessage,
                               libpq_gettext("local user with ID %d does not exist\n"),
                               (int) user_id);
     }
@@ -1125,7 +1128,7 @@ pg_fe_getauthname(PQExpBuffer errorMessage)
     {
         result = strdup(name);
         if (result == NULL && errorMessage)
-            printfPQExpBuffer(errorMessage,
+            appendPQExpBuffer(errorMessage,
                               libpq_gettext("out of memory\n"));
     }

@@ -1196,6 +1199,8 @@ PQencryptPasswordConn(PGconn *conn, const char *passwd, const char *user,
     if (!conn)
         return NULL;

+    resetPQExpBuffer(&conn->errorMessage);
+
     /* If no algorithm was given, ask the server. */
     if (algorithm == NULL)
     {
@@ -1217,7 +1222,7 @@ PQencryptPasswordConn(PGconn *conn, const char *passwd, const char *user,
         if (PQntuples(res) != 1 || PQnfields(res) != 1)
         {
             PQclear(res);
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("unexpected shape of result set returned for SHOW\n"));
             return NULL;
         }
@@ -1226,7 +1231,7 @@ PQencryptPasswordConn(PGconn *conn, const char *passwd, const char *user,
         if (strlen(val) > MAX_ALGORITHM_NAME_LEN)
         {
             PQclear(res);
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("password_encryption value too long\n"));
             return NULL;
         }
@@ -1266,14 +1271,14 @@ PQencryptPasswordConn(PGconn *conn, const char *passwd, const char *user,
     }
     else
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("unrecognized password encryption algorithm \"%s\"\n"),
                           algorithm);
         return NULL;
     }

     if (!crypt_pwd)
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("out of memory\n"));

     return crypt_pwd;
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index a834ce8cf0..84e2868104 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -755,7 +755,9 @@ PQconnectStartParams(const char *const *keywords,
     PQconninfoOption *connOptions;

     /*
-     * Allocate memory for the conn structure
+     * Allocate memory for the conn structure.  Note that we also expect this
+     * to initialize conn->errorMessage to empty.  All subsequent steps during
+     * connection initialization will only append to that buffer.
      */
     conn = makeEmptyPGconn();
     if (conn == NULL)
@@ -831,7 +833,9 @@ PQconnectStart(const char *conninfo)
     PGconn       *conn;

     /*
-     * Allocate memory for the conn structure
+     * Allocate memory for the conn structure.  Note that we also expect this
+     * to initialize conn->errorMessage to empty.  All subsequent steps during
+     * connection initialization will only append to that buffer.
      */
     conn = makeEmptyPGconn();
     if (conn == NULL)
@@ -889,7 +893,7 @@ fillPGconn(PGconn *conn, PQconninfoOption *connOptions)
                 *connmember = strdup(tmp);
                 if (*connmember == NULL)
                 {
-                    printfPQExpBuffer(&conn->errorMessage,
+                    appendPQExpBuffer(&conn->errorMessage,
                                       libpq_gettext("out of memory\n"));
                     return false;
                 }
@@ -1072,7 +1076,7 @@ connectOptions2(PGconn *conn)
         if (more || i != conn->nconnhost)
         {
             conn->status = CONNECTION_BAD;
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("could not match %d host names to %d hostaddr values\n"),
                               count_comma_separated_elems(conn->pghost), conn->nconnhost);
             return false;
@@ -1153,7 +1157,7 @@ connectOptions2(PGconn *conn)
         else if (more || i != conn->nconnhost)
         {
             conn->status = CONNECTION_BAD;
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("could not match %d port numbers to %d hosts\n"),
                               count_comma_separated_elems(conn->pgport), conn->nconnhost);
             return false;
@@ -1246,7 +1250,7 @@ connectOptions2(PGconn *conn)
             && strcmp(conn->channel_binding, "require") != 0)
         {
             conn->status = CONNECTION_BAD;
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("invalid %s value: \"%s\"\n"),
                               "channel_binding", conn->channel_binding);
             return false;
@@ -1272,7 +1276,7 @@ connectOptions2(PGconn *conn)
             && strcmp(conn->sslmode, "verify-full") != 0)
         {
             conn->status = CONNECTION_BAD;
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("invalid %s value: \"%s\"\n"),
                               "sslmode", conn->sslmode);
             return false;
@@ -1293,7 +1297,7 @@ connectOptions2(PGconn *conn)
             case 'r':            /* "require" */
             case 'v':            /* "verify-ca" or "verify-full" */
                 conn->status = CONNECTION_BAD;
-                printfPQExpBuffer(&conn->errorMessage,
+                appendPQExpBuffer(&conn->errorMessage,
                                   libpq_gettext("sslmode value \"%s\" invalid when SSL support is not compiled in\n"),
                                   conn->sslmode);
                 return false;
@@ -1314,7 +1318,7 @@ connectOptions2(PGconn *conn)
     if (!sslVerifyProtocolVersion(conn->ssl_min_protocol_version))
     {
         conn->status = CONNECTION_BAD;
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("invalid %s value: \"%s\"\n"),
                           "ssl_min_protocol_version",
                           conn->ssl_min_protocol_version);
@@ -1323,7 +1327,7 @@ connectOptions2(PGconn *conn)
     if (!sslVerifyProtocolVersion(conn->ssl_max_protocol_version))
     {
         conn->status = CONNECTION_BAD;
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("invalid %s value: \"%s\"\n"),
                           "ssl_max_protocol_version",
                           conn->ssl_max_protocol_version);
@@ -1341,7 +1345,7 @@ connectOptions2(PGconn *conn)
                                 conn->ssl_max_protocol_version))
     {
         conn->status = CONNECTION_BAD;
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("invalid SSL protocol version range\n"));
         return false;
     }
@@ -1356,7 +1360,7 @@ connectOptions2(PGconn *conn)
             strcmp(conn->gssencmode, "require") != 0)
         {
             conn->status = CONNECTION_BAD;
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("invalid %s value: \"%s\"\n"),
                               "gssencmode",
                               conn->gssencmode);
@@ -1366,7 +1370,7 @@ connectOptions2(PGconn *conn)
         if (strcmp(conn->gssencmode, "require") == 0)
         {
             conn->status = CONNECTION_BAD;
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("gssencmode value \"%s\" invalid when GSSAPI support is not compiled
in\n"),
                               conn->gssencmode);
             return false;
@@ -1401,7 +1405,7 @@ connectOptions2(PGconn *conn)
             && strcmp(conn->target_session_attrs, "read-write") != 0)
         {
             conn->status = CONNECTION_BAD;
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("invalid %s value: \"%s\"\n"),
                               "target_settion_attrs",
                               conn->target_session_attrs);
@@ -1420,7 +1424,7 @@ connectOptions2(PGconn *conn)

 oom_error:
     conn->status = CONNECTION_BAD;
-    printfPQExpBuffer(&conn->errorMessage,
+    appendPQExpBuffer(&conn->errorMessage,
                       libpq_gettext("out of memory\n"));
     return false;
 }
@@ -1487,7 +1491,9 @@ PQsetdbLogin(const char *pghost, const char *pgport, const char *pgoptions,
     PGconn       *conn;

     /*
-     * Allocate memory for the conn structure
+     * Allocate memory for the conn structure.  Note that we also expect this
+     * to initialize conn->errorMessage to empty.  All subsequent steps during
+     * connection initialization will only append to that buffer.
      */
     conn = makeEmptyPGconn();
     if (conn == NULL)
@@ -1596,7 +1602,7 @@ PQsetdbLogin(const char *pghost, const char *pgport, const char *pgoptions,

 oom_error:
     conn->status = CONNECTION_BAD;
-    printfPQExpBuffer(&conn->errorMessage,
+    appendPQExpBuffer(&conn->errorMessage,
                       libpq_gettext("out of memory\n"));
     return conn;
 }
@@ -2017,7 +2023,7 @@ connectDBStart(PGconn *conn)
      */
     if (!pg_link_canary_is_frontend())
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           "libpq is incorrectly linked to backend functions\n");
         goto connect_errReturn;
     }
@@ -2026,14 +2032,6 @@ connectDBStart(PGconn *conn)
     conn->inStart = conn->inCursor = conn->inEnd = 0;
     conn->outCount = 0;

-    /*
-     * Ensure errorMessage is empty, too.  PQconnectPoll will append messages
-     * to it in the process of scanning for a working server.  Thus, if we
-     * fail to connect to multiple hosts, the final error message will include
-     * details about each failure.
-     */
-    resetPQExpBuffer(&conn->errorMessage);
-
     /*
      * Set up to try to connect to the first host.  (Setting whichhost = -1 is
      * a bit of a cheat, but PQconnectPoll will advance it to 0 before
@@ -2139,12 +2137,6 @@ connectDBComplete(PGconn *conn)
         switch (flag)
         {
             case PGRES_POLLING_OK:
-
-                /*
-                 * Reset stored error messages since we now have a working
-                 * connection
-                 */
-                resetPQExpBuffer(&conn->errorMessage);
                 return 1;        /* success! */

             case PGRES_POLLING_READING:
@@ -2189,46 +2181,6 @@ connectDBComplete(PGconn *conn)
     }
 }

-/*
- * This subroutine saves conn->errorMessage, which will be restored back by
- * restoreErrorMessage subroutine.  Returns false on OOM failure.
- */
-static bool
-saveErrorMessage(PGconn *conn, PQExpBuffer savedMessage)
-{
-    initPQExpBuffer(savedMessage);
-    appendPQExpBufferStr(savedMessage,
-                         conn->errorMessage.data);
-    if (PQExpBufferBroken(savedMessage))
-    {
-        printfPQExpBuffer(&conn->errorMessage,
-                          libpq_gettext("out of memory\n"));
-        return false;
-    }
-    /* Clear whatever is in errorMessage now */
-    resetPQExpBuffer(&conn->errorMessage);
-    return true;
-}
-
-/*
- * Restores saved error messages back to conn->errorMessage, prepending them
- * to whatever is in conn->errorMessage already.  (This does the right thing
- * if anything's been added to conn->errorMessage since saveErrorMessage.)
- */
-static void
-restoreErrorMessage(PGconn *conn, PQExpBuffer savedMessage)
-{
-    appendPQExpBufferStr(savedMessage, conn->errorMessage.data);
-    resetPQExpBuffer(&conn->errorMessage);
-    appendPQExpBufferStr(&conn->errorMessage, savedMessage->data);
-    /* If any step above hit OOM, just report that */
-    if (PQExpBufferBroken(savedMessage) ||
-        PQExpBufferBroken(&conn->errorMessage))
-        printfPQExpBuffer(&conn->errorMessage,
-                          libpq_gettext("out of memory\n"));
-    termPQExpBuffer(savedMessage);
-}
-
 /* ----------------
  *        PQconnectPoll
  *
@@ -2264,7 +2216,6 @@ PQconnectPoll(PGconn *conn)
     PGresult   *res;
     char        sebuf[PG_STRERROR_R_BUFLEN];
     int            optval;
-    PQExpBufferData savedMessage;

     if (conn == NULL)
         return PGRES_POLLING_FAILED;
@@ -2954,11 +2905,7 @@ keep_going:                        /* We will come back to here until there is
                                                         EnvironmentOptions);
                 if (!startpacket)
                 {
-                    /*
-                     * will not appendbuffer here, since it's likely to also
-                     * run out of memory
-                     */
-                    printfPQExpBuffer(&conn->errorMessage,
+                    appendPQExpBuffer(&conn->errorMessage,
                                       libpq_gettext("out of memory\n"));
                     goto error_return;
                 }
@@ -3448,7 +3395,6 @@ keep_going:                        /* We will come back to here until there is
                  * avoid the Kerberos code doing a hostname look-up.
                  */
                 res = pg_fe_sendauth(areq, msgLength, conn);
-                conn->errorMessage.len = strlen(conn->errorMessage.data);

                 /* OK, we have processed the message; mark data consumed */
                 conn->inStart = conn->inCursor;
@@ -3576,24 +3522,16 @@ keep_going:                        /* We will come back to here until there is
                     strcmp(conn->target_session_attrs, "read-write") == 0)
                 {
                     /*
-                     * Save existing error messages across the PQsendQuery
-                     * attempt.  This is necessary because PQsendQuery is
-                     * going to reset conn->errorMessage, so we would lose
-                     * error messages related to previous hosts we have tried
-                     * and failed to connect to.
+                     * We use PQsendQueryContinue so that conn->errorMessage
+                     * does not get cleared.  We need to preserve any error
+                     * messages related to previous hosts we have tried and
+                     * failed to connect to.
                      */
-                    if (!saveErrorMessage(conn, &savedMessage))
-                        goto error_return;
-
                     conn->status = CONNECTION_OK;
-                    if (!PQsendQuery(conn,
-                                     "SHOW transaction_read_only"))
-                    {
-                        restoreErrorMessage(conn, &savedMessage);
+                    if (!PQsendQueryContinue(conn,
+                                             "SHOW transaction_read_only"))
                         goto error_return;
-                    }
                     conn->status = CONNECTION_CHECK_WRITABLE;
-                    restoreErrorMessage(conn, &savedMessage);
                     return PGRES_POLLING_READING;
                 }

@@ -3673,20 +3611,13 @@ keep_going:                        /* We will come back to here until there is
                 const char *displayed_host;
                 const char *displayed_port;

-                if (!saveErrorMessage(conn, &savedMessage))
-                    goto error_return;
-
                 conn->status = CONNECTION_OK;
                 if (!PQconsumeInput(conn))
-                {
-                    restoreErrorMessage(conn, &savedMessage);
                     goto error_return;
-                }

                 if (PQisBusy(conn))
                 {
                     conn->status = CONNECTION_CHECK_WRITABLE;
-                    restoreErrorMessage(conn, &savedMessage);
                     return PGRES_POLLING_READING;
                 }

@@ -3701,7 +3632,6 @@ keep_going:                        /* We will come back to here until there is
                     {
                         /* Not writable; fail this connection. */
                         PQclear(res);
-                        restoreErrorMessage(conn, &savedMessage);

                         /* Append error report to conn->errorMessage. */
                         if (conn->connhost[conn->whichhost].type == CHT_HOST_ADDRESS)
@@ -3732,7 +3662,6 @@ keep_going:                        /* We will come back to here until there is

                     /* Session is read-write, so we're good. */
                     PQclear(res);
-                    termPQExpBuffer(&savedMessage);

                     /*
                      * Finish reading any remaining messages before being
@@ -3748,7 +3677,6 @@ keep_going:                        /* We will come back to here until there is
                  */
                 if (res)
                     PQclear(res);
-                restoreErrorMessage(conn, &savedMessage);

                 /* Append error report to conn->errorMessage. */
                 if (conn->connhost[conn->whichhost].type == CHT_HOST_ADDRESS)
@@ -4157,6 +4085,9 @@ closePGconn(PGconn *conn)

     /*
      * Close the connection, reset all transient state, flush I/O buffers.
+     * Note that this includes clearing conn->errorMessage; we're no longer
+     * interested in any failures associated with the old connection, and we
+     * want a clean slate for any new connection attempt.
      */
     pqDropConnection(conn, true);
     conn->status = CONNECTION_BAD;    /* Well, not really _bad_ - just absent */
@@ -4212,7 +4143,7 @@ PQreset(PGconn *conn)
                                           conn->events[i].passThrough))
                 {
                     conn->status = CONNECTION_BAD;
-                    printfPQExpBuffer(&conn->errorMessage,
+                    appendPQExpBuffer(&conn->errorMessage,
                                       libpq_gettext("PGEventProc \"%s\" failed during PGEVT_CONNRESET event\n"),
                                       conn->events[i].name);
                     break;
@@ -4272,7 +4203,7 @@ PQresetPoll(PGconn *conn)
                                           conn->events[i].passThrough))
                 {
                     conn->status = CONNECTION_BAD;
-                    printfPQExpBuffer(&conn->errorMessage,
+                    appendPQExpBuffer(&conn->errorMessage,
                                       libpq_gettext("PGEventProc \"%s\" failed during PGEVT_CONNRESET event\n"),
                                       conn->events[i].name);
                     return PGRES_POLLING_FAILED;
@@ -4569,7 +4500,7 @@ pqPacketSend(PGconn *conn, char pack_type,
  *    2 if a connection could not be established, and
  *    3 if a fatal error occurred.
  *
- * An error message is returned in the third argument for return codes 1 and 3.
+ * An error message is appended to *errorMessage for return codes 1 and 3.
  */
 static int
 ldapServiceLookup(const char *purl, PQconninfoOption *options,
@@ -4607,7 +4538,7 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options,

     if ((url = strdup(purl)) == NULL)
     {
-        printfPQExpBuffer(errorMessage, libpq_gettext("out of memory\n"));
+        appendPQExpBuffer(errorMessage, libpq_gettext("out of memory\n"));
         return 3;
     }

@@ -4619,7 +4550,7 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options,

     if (pg_strncasecmp(url, LDAP_URL, strlen(LDAP_URL)) != 0)
     {
-        printfPQExpBuffer(errorMessage,
+        appendPQExpBuffer(errorMessage,
                           libpq_gettext("invalid LDAP URL \"%s\": scheme must be ldap://\n"), purl);
         free(url);
         return 3;
@@ -4634,7 +4565,7 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options,
     p = strchr(url + strlen(LDAP_URL), '/');
     if (p == NULL || *(p + 1) == '\0' || *(p + 1) == '?')
     {
-        printfPQExpBuffer(errorMessage,
+        appendPQExpBuffer(errorMessage,
                           libpq_gettext("invalid LDAP URL \"%s\": missing distinguished name\n"),
                           purl);
         free(url);
@@ -4646,7 +4577,7 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options,
     /* attribute */
     if ((p = strchr(dn, '?')) == NULL || *(p + 1) == '\0' || *(p + 1) == '?')
     {
-        printfPQExpBuffer(errorMessage,
+        appendPQExpBuffer(errorMessage,
                           libpq_gettext("invalid LDAP URL \"%s\": must have exactly one attribute\n"),
                           purl);
         free(url);
@@ -4658,7 +4589,7 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options,
     /* scope */
     if ((p = strchr(attrs[0], '?')) == NULL || *(p + 1) == '\0' || *(p + 1) == '?')
     {
-        printfPQExpBuffer(errorMessage, libpq_gettext("invalid LDAP URL \"%s\": must have search scope
(base/one/sub)\n"),purl); 
+        appendPQExpBuffer(errorMessage, libpq_gettext("invalid LDAP URL \"%s\": must have search scope
(base/one/sub)\n"),purl); 
         free(url);
         return 3;
     }
@@ -4668,7 +4599,7 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options,
     /* filter */
     if ((p = strchr(scopestr, '?')) == NULL || *(p + 1) == '\0' || *(p + 1) == '?')
     {
-        printfPQExpBuffer(errorMessage,
+        appendPQExpBuffer(errorMessage,
                           libpq_gettext("invalid LDAP URL \"%s\": no filter\n"), purl);
         free(url);
         return 3;
@@ -4689,7 +4620,7 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options,
         lport = strtol(portstr, &endptr, 10);
         if (*portstr == '\0' || *endptr != '\0' || errno || lport < 0 || lport > 65535)
         {
-            printfPQExpBuffer(errorMessage,
+            appendPQExpBuffer(errorMessage,
                               libpq_gettext("invalid LDAP URL \"%s\": invalid port number\n"),
                               purl);
             free(url);
@@ -4701,7 +4632,7 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options,
     /* Allow only one attribute */
     if (strchr(attrs[0], ',') != NULL)
     {
-        printfPQExpBuffer(errorMessage,
+        appendPQExpBuffer(errorMessage,
                           libpq_gettext("invalid LDAP URL \"%s\": must have exactly one attribute\n"),
                           purl);
         free(url);
@@ -4717,7 +4648,7 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options,
         scope = LDAP_SCOPE_SUBTREE;
     else
     {
-        printfPQExpBuffer(errorMessage, libpq_gettext("invalid LDAP URL \"%s\": must have search scope
(base/one/sub)\n"),purl); 
+        appendPQExpBuffer(errorMessage, libpq_gettext("invalid LDAP URL \"%s\": must have search scope
(base/one/sub)\n"),purl); 
         free(url);
         return 3;
     }
@@ -4725,7 +4656,7 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options,
     /* initialize LDAP structure */
     if ((ld = ldap_init(hostname, port)) == NULL)
     {
-        printfPQExpBuffer(errorMessage,
+        appendPQExpBuffer(errorMessage,
                           libpq_gettext("could not create LDAP structure\n"));
         free(url);
         return 3;
@@ -4801,7 +4732,7 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options,
     {
         if (res != NULL)
             ldap_msgfree(res);
-        printfPQExpBuffer(errorMessage,
+        appendPQExpBuffer(errorMessage,
                           libpq_gettext("lookup on LDAP server failed: %s\n"),
                           ldap_err2string(rc));
         ldap_unbind(ld);
@@ -4812,7 +4743,7 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options,
     /* complain if there was not exactly one result */
     if ((rc = ldap_count_entries(ld, res)) != 1)
     {
-        printfPQExpBuffer(errorMessage,
+        appendPQExpBuffer(errorMessage,
                           rc ? libpq_gettext("more than one entry found on LDAP lookup\n")
                           : libpq_gettext("no entry found on LDAP lookup\n"));
         ldap_msgfree(res);
@@ -4825,7 +4756,7 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options,
     if ((entry = ldap_first_entry(ld, res)) == NULL)
     {
         /* should never happen */
-        printfPQExpBuffer(errorMessage,
+        appendPQExpBuffer(errorMessage,
                           libpq_gettext("no entry found on LDAP lookup\n"));
         ldap_msgfree(res);
         ldap_unbind(ld);
@@ -4836,7 +4767,7 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options,
     /* get values */
     if ((values = ldap_get_values_len(ld, entry, attrs[0])) == NULL)
     {
-        printfPQExpBuffer(errorMessage,
+        appendPQExpBuffer(errorMessage,
                           libpq_gettext("attribute has no values on LDAP lookup\n"));
         ldap_msgfree(res);
         ldap_unbind(ld);
@@ -4849,7 +4780,7 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options,

     if (values[0] == NULL)
     {
-        printfPQExpBuffer(errorMessage,
+        appendPQExpBuffer(errorMessage,
                           libpq_gettext("attribute has no values on LDAP lookup\n"));
         ldap_value_free_len(values);
         ldap_unbind(ld);
@@ -4862,7 +4793,7 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options,
         size += values[i]->bv_len + 1;
     if ((result = malloc(size)) == NULL)
     {
-        printfPQExpBuffer(errorMessage,
+        appendPQExpBuffer(errorMessage,
                           libpq_gettext("out of memory\n"));
         ldap_value_free_len(values);
         ldap_unbind(ld);
@@ -4901,7 +4832,7 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options,
                 }
                 else if (ld_is_nl_cr(*p))
                 {
-                    printfPQExpBuffer(errorMessage,
+                    appendPQExpBuffer(errorMessage,
                                       libpq_gettext("missing \"=\" after \"%s\" in connection info string\n"),
                                       optname);
                     free(result);
@@ -4920,7 +4851,7 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options,
                 }
                 else if (!ld_is_sp_tab(*p))
                 {
-                    printfPQExpBuffer(errorMessage,
+                    appendPQExpBuffer(errorMessage,
                                       libpq_gettext("missing \"=\" after \"%s\" in connection info string\n"),
                                       optname);
                     free(result);
@@ -4981,7 +4912,7 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options,
                         options[i].val = strdup(optval);
                         if (!options[i].val)
                         {
-                            printfPQExpBuffer(errorMessage,
+                            appendPQExpBuffer(errorMessage,
                                               libpq_gettext("out of memory\n"));
                             free(result);
                             return 3;
@@ -4993,7 +4924,7 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options,
             }
             if (!found_keyword)
             {
-                printfPQExpBuffer(errorMessage,
+                appendPQExpBuffer(errorMessage,
                                   libpq_gettext("invalid connection option \"%s\"\n"),
                                   optname);
                 free(result);
@@ -5009,7 +4940,7 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options,

     if (state == 5 || state == 6)
     {
-        printfPQExpBuffer(errorMessage,
+        appendPQExpBuffer(errorMessage,
                           libpq_gettext("unterminated quoted string in connection info string\n"));
         return 3;
     }
@@ -5090,7 +5021,7 @@ next_file:
 last_file:
     if (!group_found)
     {
-        printfPQExpBuffer(errorMessage,
+        appendPQExpBuffer(errorMessage,
                           libpq_gettext("definition of service \"%s\" not found\n"), service);
         return 3;
     }
@@ -5117,7 +5048,7 @@ parseServiceFile(const char *serviceFile,
     f = fopen(serviceFile, "r");
     if (f == NULL)
     {
-        printfPQExpBuffer(errorMessage, libpq_gettext("service file \"%s\" not found\n"),
+        appendPQExpBuffer(errorMessage, libpq_gettext("service file \"%s\" not found\n"),
                           serviceFile);
         return 1;
     }
@@ -5193,7 +5124,7 @@ parseServiceFile(const char *serviceFile,
                 val = strchr(line, '=');
                 if (val == NULL)
                 {
-                    printfPQExpBuffer(errorMessage,
+                    appendPQExpBuffer(errorMessage,
                                       libpq_gettext("syntax error in service file \"%s\", line %d\n"),
                                       serviceFile,
                                       linenr);
@@ -5204,7 +5135,7 @@ parseServiceFile(const char *serviceFile,

                 if (strcmp(key, "service") == 0)
                 {
-                    printfPQExpBuffer(errorMessage,
+                    appendPQExpBuffer(errorMessage,
                                       libpq_gettext("nested service specifications not supported in service file
\"%s\",line %d\n"), 
                                       serviceFile,
                                       linenr);
@@ -5225,7 +5156,7 @@ parseServiceFile(const char *serviceFile,
                             options[i].val = strdup(val);
                         if (!options[i].val)
                         {
-                            printfPQExpBuffer(errorMessage,
+                            appendPQExpBuffer(errorMessage,
                                               libpq_gettext("out of memory\n"));
                             result = 3;
                             goto exit;
@@ -5237,7 +5168,7 @@ parseServiceFile(const char *serviceFile,

                 if (!found_keyword)
                 {
-                    printfPQExpBuffer(errorMessage,
+                    appendPQExpBuffer(errorMessage,
                                       libpq_gettext("syntax error in service file \"%s\", line %d\n"),
                                       serviceFile,
                                       linenr);
@@ -5307,7 +5238,7 @@ conninfo_init(PQExpBuffer errorMessage)
     options = (PQconninfoOption *) malloc(sizeof(PQconninfoOption) * sizeof(PQconninfoOptions) /
sizeof(PQconninfoOptions[0]));
     if (options == NULL)
     {
-        printfPQExpBuffer(errorMessage,
+        appendPQExpBuffer(errorMessage,
                           libpq_gettext("out of memory\n"));
         return NULL;
     }
@@ -5328,7 +5259,7 @@ conninfo_init(PQExpBuffer errorMessage)
  * Connection string parser
  *
  * Returns a malloc'd PQconninfoOption array, if parsing is successful.
- * Otherwise, NULL is returned and an error message is left in errorMessage.
+ * Otherwise, NULL is returned and an error message is added to errorMessage.
  *
  * If use_defaults is true, default values are filled in (from a service file,
  * environment variables, etc).
@@ -5406,7 +5337,7 @@ conninfo_parse(const char *conninfo, PQExpBuffer errorMessage,
     /* Need a modifiable copy of the input string */
     if ((buf = strdup(conninfo)) == NULL)
     {
-        printfPQExpBuffer(errorMessage,
+        appendPQExpBuffer(errorMessage,
                           libpq_gettext("out of memory\n"));
         PQconninfoFree(options);
         return NULL;
@@ -5445,7 +5376,7 @@ conninfo_parse(const char *conninfo, PQExpBuffer errorMessage,
         /* Check that there is a following '=' */
         if (*cp != '=')
         {
-            printfPQExpBuffer(errorMessage,
+            appendPQExpBuffer(errorMessage,
                               libpq_gettext("missing \"=\" after \"%s\" in connection info string\n"),
                               pname);
             PQconninfoFree(options);
@@ -5494,7 +5425,7 @@ conninfo_parse(const char *conninfo, PQExpBuffer errorMessage,
             {
                 if (*cp == '\0')
                 {
-                    printfPQExpBuffer(errorMessage,
+                    appendPQExpBuffer(errorMessage,
                                       libpq_gettext("unterminated quoted string in connection info string\n"));
                     PQconninfoFree(options);
                     free(buf);
@@ -5551,7 +5482,7 @@ conninfo_parse(const char *conninfo, PQExpBuffer errorMessage,
  *
  * If successful, a malloc'd PQconninfoOption array is returned.
  * If not successful, NULL is returned and an error message is
- * left in errorMessage.
+ * appended to errorMessage.
  * Defaults are supplied (from a service file, environment variables, etc)
  * for unspecified options, but only if use_defaults is true.
  *
@@ -5630,7 +5561,7 @@ conninfo_array_parse(const char *const *keywords, const char *const *values,
             /* Check for invalid connection option */
             if (option->keyword == NULL)
             {
-                printfPQExpBuffer(errorMessage,
+                appendPQExpBuffer(errorMessage,
                                   libpq_gettext("invalid connection option \"%s\"\n"),
                                   pname);
                 PQconninfoFree(options);
@@ -5662,7 +5593,7 @@ conninfo_array_parse(const char *const *keywords, const char *const *values,
                                 options[k].val = strdup(str_option->val);
                                 if (!options[k].val)
                                 {
-                                    printfPQExpBuffer(errorMessage,
+                                    appendPQExpBuffer(errorMessage,
                                                       libpq_gettext("out of memory\n"));
                                     PQconninfoFree(options);
                                     PQconninfoFree(dbname_options);
@@ -5691,7 +5622,7 @@ conninfo_array_parse(const char *const *keywords, const char *const *values,
                 option->val = strdup(pvalue);
                 if (!option->val)
                 {
-                    printfPQExpBuffer(errorMessage,
+                    appendPQExpBuffer(errorMessage,
                                       libpq_gettext("out of memory\n"));
                     PQconninfoFree(options);
                     PQconninfoFree(dbname_options);
@@ -5763,7 +5694,7 @@ conninfo_add_defaults(PQconninfoOption *options, PQExpBuffer errorMessage)
                 if (!option->val)
                 {
                     if (errorMessage)
-                        printfPQExpBuffer(errorMessage,
+                        appendPQExpBuffer(errorMessage,
                                           libpq_gettext("out of memory\n"));
                     return false;
                 }
@@ -5787,7 +5718,7 @@ conninfo_add_defaults(PQconninfoOption *options, PQExpBuffer errorMessage)
                 if (!option->val)
                 {
                     if (errorMessage)
-                        printfPQExpBuffer(errorMessage,
+                        appendPQExpBuffer(errorMessage,
                                           libpq_gettext("out of memory\n"));
                     return false;
                 }
@@ -5805,7 +5736,7 @@ conninfo_add_defaults(PQconninfoOption *options, PQExpBuffer errorMessage)
             if (!option->val)
             {
                 if (errorMessage)
-                    printfPQExpBuffer(errorMessage,
+                    appendPQExpBuffer(errorMessage,
                                       libpq_gettext("out of memory\n"));
                 return false;
             }
@@ -5906,7 +5837,7 @@ conninfo_uri_parse_options(PQconninfoOption *options, const char *uri,
     initPQExpBuffer(&portbuf);
     if (PQExpBufferDataBroken(hostbuf) || PQExpBufferDataBroken(portbuf))
     {
-        printfPQExpBuffer(errorMessage,
+        appendPQExpBuffer(errorMessage,
                           libpq_gettext("out of memory\n"));
         goto cleanup;
     }
@@ -5915,7 +5846,7 @@ conninfo_uri_parse_options(PQconninfoOption *options, const char *uri,
     buf = strdup(uri);
     if (buf == NULL)
     {
-        printfPQExpBuffer(errorMessage,
+        appendPQExpBuffer(errorMessage,
                           libpq_gettext("out of memory\n"));
         goto cleanup;
     }
@@ -5926,7 +5857,7 @@ conninfo_uri_parse_options(PQconninfoOption *options, const char *uri,
     if (prefix_len == 0)
     {
         /* Should never happen */
-        printfPQExpBuffer(errorMessage,
+        appendPQExpBuffer(errorMessage,
                           libpq_gettext("invalid URI propagated to internal parser routine: \"%s\"\n"),
                           uri);
         goto cleanup;
@@ -6003,14 +5934,14 @@ conninfo_uri_parse_options(PQconninfoOption *options, const char *uri,
                 ++p;
             if (!*p)
             {
-                printfPQExpBuffer(errorMessage,
+                appendPQExpBuffer(errorMessage,
                                   libpq_gettext("end of string reached when looking for matching \"]\" in IPv6 host
addressin URI: \"%s\"\n"), 
                                   uri);
                 goto cleanup;
             }
             if (p == host)
             {
-                printfPQExpBuffer(errorMessage,
+                appendPQExpBuffer(errorMessage,
                                   libpq_gettext("IPv6 host address may not be empty in URI: \"%s\"\n"),
                                   uri);
                 goto cleanup;
@@ -6025,7 +5956,7 @@ conninfo_uri_parse_options(PQconninfoOption *options, const char *uri,
              */
             if (*p && *p != ':' && *p != '/' && *p != '?' && *p != ',')
             {
-                printfPQExpBuffer(errorMessage,
+                appendPQExpBuffer(errorMessage,
                                   libpq_gettext("unexpected character \"%c\" at position %d in URI (expected \":\" or
\"/\"):\"%s\"\n"), 
                                   *p, (int) (p - buf + 1), uri);
                 goto cleanup;
@@ -6142,6 +6073,7 @@ conninfo_uri_parse_params(char *params,
         char       *value = NULL;
         char       *p = params;
         bool        malloced = false;
+        int            oldmsglen;

         /*
          * Scan the params string for '=' and '&', marking the end of keyword
@@ -6154,7 +6086,7 @@ conninfo_uri_parse_params(char *params,
                 /* Was there '=' already? */
                 if (value != NULL)
                 {
-                    printfPQExpBuffer(errorMessage,
+                    appendPQExpBuffer(errorMessage,
                                       libpq_gettext("extra key/value separator \"=\" in URI query parameter:
\"%s\"\n"),
                                       keyword);
                     return false;
@@ -6174,7 +6106,7 @@ conninfo_uri_parse_params(char *params,
                 /* Was there '=' at all? */
                 if (value == NULL)
                 {
-                    printfPQExpBuffer(errorMessage,
+                    appendPQExpBuffer(errorMessage,
                                       libpq_gettext("missing key/value separator \"=\" in URI query parameter:
\"%s\"\n"),
                                       keyword);
                     return false;
@@ -6220,12 +6152,13 @@ conninfo_uri_parse_params(char *params,
          * otherwise.  At this point both keyword and value are not
          * URI-encoded.
          */
+        oldmsglen = errorMessage->len;
         if (!conninfo_storeval(connOptions, keyword, value,
                                errorMessage, true, false))
         {
             /* Insert generic message if conninfo_storeval didn't give one. */
-            if (errorMessage->len == 0)
-                printfPQExpBuffer(errorMessage,
+            if (errorMessage->len == oldmsglen)
+                appendPQExpBuffer(errorMessage,
                                   libpq_gettext("invalid URI query parameter: \"%s\"\n"),
                                   keyword);
             /* And fail. */
@@ -6272,7 +6205,7 @@ conninfo_uri_decode(const char *str, PQExpBuffer errorMessage)
     buf = malloc(strlen(str) + 1);
     if (buf == NULL)
     {
-        printfPQExpBuffer(errorMessage, libpq_gettext("out of memory\n"));
+        appendPQExpBuffer(errorMessage, libpq_gettext("out of memory\n"));
         return NULL;
     }
     p = buf;
@@ -6299,7 +6232,7 @@ conninfo_uri_decode(const char *str, PQExpBuffer errorMessage)
              */
             if (!(get_hexdigit(*q++, &hi) && get_hexdigit(*q++, &lo)))
             {
-                printfPQExpBuffer(errorMessage,
+                appendPQExpBuffer(errorMessage,
                                   libpq_gettext("invalid percent-encoded token: \"%s\"\n"),
                                   str);
                 free(buf);
@@ -6309,7 +6242,7 @@ conninfo_uri_decode(const char *str, PQExpBuffer errorMessage)
             c = (hi << 4) | lo;
             if (c == 0)
             {
-                printfPQExpBuffer(errorMessage,
+                appendPQExpBuffer(errorMessage,
                                   libpq_gettext("forbidden value %%00 in percent-encoded value: \"%s\"\n"),
                                   str);
                 free(buf);
@@ -6404,7 +6337,7 @@ conninfo_storeval(PQconninfoOption *connOptions,
     if (option == NULL)
     {
         if (!ignoreMissing)
-            printfPQExpBuffer(errorMessage,
+            appendPQExpBuffer(errorMessage,
                               libpq_gettext("invalid connection option \"%s\"\n"),
                               keyword);
         return NULL;
@@ -6422,7 +6355,7 @@ conninfo_storeval(PQconninfoOption *connOptions,
         value_copy = strdup(value);
         if (value_copy == NULL)
         {
-            printfPQExpBuffer(errorMessage, libpq_gettext("out of memory\n"));
+            appendPQExpBuffer(errorMessage, libpq_gettext("out of memory\n"));
             return NULL;
         }
     }
@@ -6469,7 +6402,10 @@ PQconninfo(PGconn *conn)
     if (conn == NULL)
         return NULL;

-    /* We don't actually report any errors here, but callees want a buffer */
+    /*
+     * We don't actually report any errors here, but callees want a buffer,
+     * and we prefer not to trash the conn's errorMessage.
+     */
     initPQExpBuffer(&errorBuf);
     if (PQExpBufferDataBroken(errorBuf))
         return NULL;            /* out of memory already :-( */
diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c
index d48f0fd587..39f7fefb3a 100644
--- a/src/interfaces/libpq/fe-exec.c
+++ b/src/interfaces/libpq/fe-exec.c
@@ -53,7 +53,8 @@ static bool static_std_strings = false;
 static PGEvent *dupEvents(PGEvent *events, int count, size_t *memSize);
 static bool pqAddTuple(PGresult *res, PGresAttValue *tup,
                        const char **errmsgp);
-static bool PQsendQueryStart(PGconn *conn);
+static int    PQsendQueryInternal(PGconn *conn, const char *query, bool newQuery);
+static bool PQsendQueryStart(PGconn *conn, bool newQuery);
 static int    PQsendQueryGuts(PGconn *conn,
                             const char *command,
                             const char *stmtName,
@@ -667,25 +668,6 @@ pqSetResultError(PGresult *res, const char *msg)
         res->errMsg = NULL;
 }

-/*
- * pqCatenateResultError -
- *        concatenate a new error message to the one already in a PGresult
- */
-void
-pqCatenateResultError(PGresult *res, const char *msg)
-{
-    PQExpBufferData errorBuf;
-
-    if (!res || !msg)
-        return;
-    initPQExpBuffer(&errorBuf);
-    if (res->errMsg)
-        appendPQExpBufferStr(&errorBuf, res->errMsg);
-    appendPQExpBufferStr(&errorBuf, msg);
-    pqSetResultError(res, errorBuf.data);
-    termPQExpBuffer(&errorBuf);
-}
-
 /*
  * PQclear -
  *      free's the memory associated with a PGresult
@@ -759,68 +741,46 @@ pqClearAsyncResult(PGconn *conn)
 /*
  * This subroutine deletes any existing async result, sets conn->result
  * to a PGresult with status PGRES_FATAL_ERROR, and stores the current
- * contents of conn->errorMessage into that result.  It differs from a
- * plain call on PQmakeEmptyPGresult() in that if there is already an
- * async result with status PGRES_FATAL_ERROR, the current error message
- * is APPENDED to the old error message instead of replacing it.  This
- * behavior lets us report multiple error conditions properly, if necessary.
- * (An example where this is needed is when the backend sends an 'E' message
- * and immediately closes the connection --- we want to report both the
- * backend error and the connection closure error.)
+ * contents of conn->errorMessage into that result.
  */
 void
 pqSaveErrorResult(PGconn *conn)
 {
-    /*
-     * If no old async result, just let PQmakeEmptyPGresult make one. Likewise
-     * if old result is not an error message.
-     */
-    if (conn->result == NULL ||
-        conn->result->resultStatus != PGRES_FATAL_ERROR ||
-        conn->result->errMsg == NULL)
-    {
-        pqClearAsyncResult(conn);
-        conn->result = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR);
-    }
-    else
-    {
-        /* Else, concatenate error message to existing async result. */
-        pqCatenateResultError(conn->result, conn->errorMessage.data);
-    }
+    pqClearAsyncResult(conn);
+    conn->result = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR);
 }

 /*
- * As above, and append conn->write_err_msg to whatever other error we have.
- * This is used when we've detected a write failure and have exhausted our
- * chances of reporting something else instead.
+ * As above, after appending conn->write_err_msg to whatever other error we
+ * have.  This is used when we've detected a write failure and have exhausted
+ * our chances of reporting something else instead.
  */
 static void
 pqSaveWriteError(PGconn *conn)
 {
     /*
-     * Ensure conn->result is an error result, and add anything in
-     * conn->errorMessage to it.
+     * If write_err_msg is null because of previous strdup failure, do what we
+     * can.  (It's likely our machinations here will get OOM failures as well,
+     * but might as well try.)
      */
-    pqSaveErrorResult(conn);
-
-    /*
-     * Now append write_err_msg to that.  If it's null because of previous
-     * strdup failure, do what we can.  (It's likely our machinations here are
-     * all getting OOM failures as well, but ...)
-     */
-    if (conn->write_err_msg && conn->write_err_msg[0] != '\0')
-        pqCatenateResultError(conn->result, conn->write_err_msg);
+    if (conn->write_err_msg)
+    {
+        appendPQExpBufferStr(&conn->errorMessage, conn->write_err_msg);
+        /* Avoid possibly appending the same message twice */
+        conn->write_err_msg[0] = '\0';
+    }
     else
-        pqCatenateResultError(conn->result,
-                              libpq_gettext("write to server failed\n"));
+        appendPQExpBufferStr(&conn->errorMessage,
+                             libpq_gettext("write to server failed\n"));
+
+    pqSaveErrorResult(conn);
 }

 /*
  * This subroutine prepares an async result object for return to the caller.
  * If there is not already an async result object, build an error object
  * using whatever is in conn->errorMessage.  In any case, clear the async
- * result storage and make sure PQerrorMessage will agree with the result's
- * error string.
+ * result storage.
  */
 PGresult *
 pqPrepareAsyncResult(PGconn *conn)
@@ -835,16 +795,6 @@ pqPrepareAsyncResult(PGconn *conn)
     res = conn->result;
     if (!res)
         res = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR);
-    else
-    {
-        /*
-         * Make sure PQerrorMessage agrees with result; it could be different
-         * if we have concatenated messages.
-         */
-        resetPQExpBuffer(&conn->errorMessage);
-        appendPQExpBufferStr(&conn->errorMessage,
-                             PQresultErrorMessage(res));
-    }

     /*
      * Replace conn->result with next_result, if any.  In the normal case
@@ -1229,17 +1179,32 @@ fail:
  *
  * Returns: 1 if successfully submitted
  *            0 if error (conn->errorMessage is set)
+ *
+ * PQsendQueryContinue is a non-exported version that behaves identically
+ * except that it doesn't reset conn->errorMessage.
  */
 int
 PQsendQuery(PGconn *conn, const char *query)
 {
-    if (!PQsendQueryStart(conn))
+    return PQsendQueryInternal(conn, query, true);
+}
+
+int
+PQsendQueryContinue(PGconn *conn, const char *query)
+{
+    return PQsendQueryInternal(conn, query, false);
+}
+
+static int
+PQsendQueryInternal(PGconn *conn, const char *query, bool newQuery)
+{
+    if (!PQsendQueryStart(conn, newQuery))
         return 0;

     /* check the argument */
     if (!query)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("command string is a null pointer\n"));
         return 0;
     }
@@ -1291,19 +1256,19 @@ PQsendQueryParams(PGconn *conn,
                   const int *paramFormats,
                   int resultFormat)
 {
-    if (!PQsendQueryStart(conn))
+    if (!PQsendQueryStart(conn, true))
         return 0;

     /* check the arguments */
     if (!command)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("command string is a null pointer\n"));
         return 0;
     }
     if (nParams < 0 || nParams > 65535)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("number of parameters must be between 0 and 65535\n"));
         return 0;
     }
@@ -1331,25 +1296,25 @@ PQsendPrepare(PGconn *conn,
               const char *stmtName, const char *query,
               int nParams, const Oid *paramTypes)
 {
-    if (!PQsendQueryStart(conn))
+    if (!PQsendQueryStart(conn, true))
         return 0;

     /* check the arguments */
     if (!stmtName)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("statement name is a null pointer\n"));
         return 0;
     }
     if (!query)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("command string is a null pointer\n"));
         return 0;
     }
     if (nParams < 0 || nParams > 65535)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("number of parameters must be between 0 and 65535\n"));
         return 0;
     }
@@ -1357,7 +1322,7 @@ PQsendPrepare(PGconn *conn,
     /* This isn't gonna work on a 2.0 server */
     if (PG_PROTOCOL_MAJOR(conn->pversion) < 3)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("function requires at least protocol version 3.0\n"));
         return 0;
     }
@@ -1432,19 +1397,19 @@ PQsendQueryPrepared(PGconn *conn,
                     const int *paramFormats,
                     int resultFormat)
 {
-    if (!PQsendQueryStart(conn))
+    if (!PQsendQueryStart(conn, true))
         return 0;

     /* check the arguments */
     if (!stmtName)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("statement name is a null pointer\n"));
         return 0;
     }
     if (nParams < 0 || nParams > 65535)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("number of parameters must be between 0 and 65535\n"));
         return 0;
     }
@@ -1464,25 +1429,28 @@ PQsendQueryPrepared(PGconn *conn,
  * Common startup code for PQsendQuery and sibling routines
  */
 static bool
-PQsendQueryStart(PGconn *conn)
+PQsendQueryStart(PGconn *conn, bool newQuery)
 {
     if (!conn)
         return false;

-    /* clear the error string */
-    resetPQExpBuffer(&conn->errorMessage);
+    /*
+     * If this is the beginning of a query cycle, reset the error buffer.
+     */
+    if (newQuery)
+        resetPQExpBuffer(&conn->errorMessage);

     /* Don't try to send if we know there's no live connection. */
     if (conn->status != CONNECTION_OK)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("no connection to the server\n"));
         return false;
     }
     /* Can't send while already busy, either. */
     if (conn->asyncStatus != PGASYNC_IDLE)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("another command is already in progress\n"));
         return false;
     }
@@ -1520,7 +1488,7 @@ PQsendQueryGuts(PGconn *conn,
     /* This isn't gonna work on a 2.0 server */
     if (PG_PROTOCOL_MAJOR(conn->pversion) < 3)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("function requires at least protocol version 3.0\n"));
         return 0;
     }
@@ -1596,7 +1564,7 @@ PQsendQueryGuts(PGconn *conn,
                     nbytes = paramLengths[i];
                 else
                 {
-                    printfPQExpBuffer(&conn->errorMessage,
+                    appendPQExpBuffer(&conn->errorMessage,
                                       libpq_gettext("length must be given for binary parameter\n"));
                     goto sendFailed;
                 }
@@ -1859,7 +1827,7 @@ PQgetResult(PGconn *conn)
             res = getCopyResult(conn, PGRES_COPY_BOTH);
             break;
         default:
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("unexpected asyncStatus: %d\n"),
                               (int) conn->asyncStatus);
             res = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR);
@@ -1879,7 +1847,7 @@ PQgetResult(PGconn *conn)
             if (!res->events[i].proc(PGEVT_RESULTCREATE, &evt,
                                      res->events[i].passThrough))
             {
-                printfPQExpBuffer(&conn->errorMessage,
+                appendPQExpBuffer(&conn->errorMessage,
                                   libpq_gettext("PGEventProc \"%s\" failed during PGEVT_RESULTCREATE event\n"),
                                   res->events[i].name);
                 pqSetResultError(res, conn->errorMessage.data);
@@ -2025,6 +1993,11 @@ PQexecStart(PGconn *conn)
     if (!conn)
         return false;

+    /*
+     * Since this is the beginning of a query cycle, reset the error buffer.
+     */
+    resetPQExpBuffer(&conn->errorMessage);
+
     /*
      * Silently discard any prior query result that application didn't eat.
      * This is probably poor design, but it's here for backward compatibility.
@@ -2047,7 +2020,7 @@ PQexecStart(PGconn *conn)
             else
             {
                 /* In older protocols we have to punt */
-                printfPQExpBuffer(&conn->errorMessage,
+                appendPQExpBuffer(&conn->errorMessage,
                                   libpq_gettext("COPY IN state must be terminated first\n"));
                 return false;
             }
@@ -2067,7 +2040,7 @@ PQexecStart(PGconn *conn)
             else
             {
                 /* In older protocols we have to punt */
-                printfPQExpBuffer(&conn->errorMessage,
+                appendPQExpBuffer(&conn->errorMessage,
                                   libpq_gettext("COPY OUT state must be terminated first\n"));
                 return false;
             }
@@ -2075,7 +2048,7 @@ PQexecStart(PGconn *conn)
         else if (resultStatus == PGRES_COPY_BOTH)
         {
             /* We don't allow PQexec during COPY BOTH */
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("PQexec not allowed during COPY BOTH\n"));
             return false;
         }
@@ -2099,8 +2072,9 @@ PQexecFinish(PGconn *conn)

     /*
      * For backwards compatibility, return the last result if there are more
-     * than one --- but merge error messages if we get more than one error
-     * result.
+     * than one.  (We used to have logic here to concatenate successive error
+     * messages, but now that happens automatically, since conn->errorMessage
+     * will continue to accumulate errors throughout this loop.)
      *
      * We have to stop if we see copy in/out/both, however. We will resume
      * parsing after application performs the data transfer.
@@ -2111,23 +2085,7 @@ PQexecFinish(PGconn *conn)
     while ((result = PQgetResult(conn)) != NULL)
     {
         if (lastResult)
-        {
-            if (lastResult->resultStatus == PGRES_FATAL_ERROR &&
-                result->resultStatus == PGRES_FATAL_ERROR)
-            {
-                pqCatenateResultError(lastResult, result->errMsg);
-                PQclear(result);
-                result = lastResult;
-
-                /*
-                 * Make sure PQerrorMessage agrees with concatenated result
-                 */
-                resetPQExpBuffer(&conn->errorMessage);
-                appendPQExpBufferStr(&conn->errorMessage, result->errMsg);
-            }
-            else
-                PQclear(lastResult);
-        }
+            PQclear(lastResult);
         lastResult = result;
         if (result->resultStatus == PGRES_COPY_IN ||
             result->resultStatus == PGRES_COPY_OUT ||
@@ -2223,13 +2181,13 @@ PQsendDescribe(PGconn *conn, char desc_type, const char *desc_target)
     if (!desc_target)
         desc_target = "";

-    if (!PQsendQueryStart(conn))
+    if (!PQsendQueryStart(conn, true))
         return 0;

     /* This isn't gonna work on a 2.0 server */
     if (PG_PROTOCOL_MAJOR(conn->pversion) < 3)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("function requires at least protocol version 3.0\n"));
         return 0;
     }
@@ -2321,7 +2279,7 @@ PQputCopyData(PGconn *conn, const char *buffer, int nbytes)
     if (conn->asyncStatus != PGASYNC_COPY_IN &&
         conn->asyncStatus != PGASYNC_COPY_BOTH)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("no COPY in progress\n"));
         return -1;
     }
@@ -2388,7 +2346,7 @@ PQputCopyEnd(PGconn *conn, const char *errormsg)
     if (conn->asyncStatus != PGASYNC_COPY_IN &&
         conn->asyncStatus != PGASYNC_COPY_BOTH)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("no COPY in progress\n"));
         return -1;
     }
@@ -2431,7 +2389,7 @@ PQputCopyEnd(PGconn *conn, const char *errormsg)
         if (errormsg)
         {
             /* Oops, no way to do this in 2.0 */
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("function requires at least protocol version 3.0\n"));
             return -1;
         }
@@ -2450,7 +2408,6 @@ PQputCopyEnd(PGconn *conn, const char *errormsg)
         conn->asyncStatus = PGASYNC_COPY_OUT;
     else
         conn->asyncStatus = PGASYNC_BUSY;
-    resetPQExpBuffer(&conn->errorMessage);

     /* Try to flush data */
     if (pqFlush(conn) < 0)
@@ -2478,7 +2435,7 @@ PQgetCopyData(PGconn *conn, char **buffer, int async)
     if (conn->asyncStatus != PGASYNC_COPY_OUT &&
         conn->asyncStatus != PGASYNC_COPY_BOTH)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("no COPY in progress\n"));
         return -2;
     }
@@ -2662,13 +2619,15 @@ PQfn(PGconn *conn,
     if (!conn)
         return NULL;

-    /* clear the error string */
+    /*
+     * Since this is the beginning of a query cycle, reset the error buffer.
+     */
     resetPQExpBuffer(&conn->errorMessage);

     if (conn->sock == PGINVALID_SOCKET || conn->asyncStatus != PGASYNC_IDLE ||
         conn->result != NULL)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("connection in wrong state\n"));
         return NULL;
     }
@@ -3246,7 +3205,11 @@ PQsetnonblocking(PGconn *conn, int arg)
      * need to flush the send queue at this point in order to guarantee proper
      * behavior. this is ok because either they are making a transition _from_
      * or _to_ blocking mode, either way we can block them.
+     *
+     * Clear errorMessage in case pqFlush adds to it.
      */
+    resetPQExpBuffer(&conn->errorMessage);
+
     /* if we are going from blocking to non-blocking flush here */
     if (pqFlush(conn))
         return -1;
@@ -3388,7 +3351,7 @@ PQescapeStringInternal(PGconn *conn,
             if (error)
                 *error = 1;
             if (conn)
-                printfPQExpBuffer(&conn->errorMessage,
+                appendPQExpBuffer(&conn->errorMessage,
                                   libpq_gettext("incomplete multibyte character\n"));
             for (; i < len; i++)
             {
@@ -3419,6 +3382,9 @@ PQescapeStringConn(PGconn *conn,
             *error = 1;
         return 0;
     }
+
+    resetPQExpBuffer(&conn->errorMessage);
+
     return PQescapeStringInternal(conn, to, from, length, error,
                                   conn->client_encoding,
                                   conn->std_strings);
@@ -3455,6 +3421,8 @@ PQescapeInternal(PGconn *conn, const char *str, size_t len, bool as_ident)
     if (!conn)
         return NULL;

+    resetPQExpBuffer(&conn->errorMessage);
+
     /* Scan the string for characters that must be escaped. */
     for (s = str; (s - str) < len && *s != '\0'; ++s)
     {
@@ -3472,7 +3440,7 @@ PQescapeInternal(PGconn *conn, const char *str, size_t len, bool as_ident)
             /* Multibyte character overruns allowable length. */
             if ((s - str) + charlen > len || memchr(s, 0, charlen) != NULL)
             {
-                printfPQExpBuffer(&conn->errorMessage,
+                appendPQExpBuffer(&conn->errorMessage,
                                   libpq_gettext("incomplete multibyte character\n"));
                 return NULL;
             }
@@ -3490,7 +3458,7 @@ PQescapeInternal(PGconn *conn, const char *str, size_t len, bool as_ident)
     result = rp = (char *) malloc(result_size);
     if (rp == NULL)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("out of memory\n"));
         return NULL;
     }
@@ -3655,7 +3623,7 @@ PQescapeByteaInternal(PGconn *conn,
     if (rp == NULL)
     {
         if (conn)
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("out of memory\n"));
         return NULL;
     }
@@ -3717,6 +3685,9 @@ PQescapeByteaConn(PGconn *conn,
 {
     if (!conn)
         return NULL;
+
+    resetPQExpBuffer(&conn->errorMessage);
+
     return PQescapeByteaInternal(conn, from, from_length, to_length,
                                  conn->std_strings,
                                  (conn->sversion >= 90000));
diff --git a/src/interfaces/libpq/fe-gssapi-common.c b/src/interfaces/libpq/fe-gssapi-common.c
index c2e79bb55c..b7661dd659 100644
--- a/src/interfaces/libpq/fe-gssapi-common.c
+++ b/src/interfaces/libpq/fe-gssapi-common.c
@@ -46,7 +46,6 @@ void
 pg_GSS_error(const char *mprefix, PGconn *conn,
              OM_uint32 maj_stat, OM_uint32 min_stat)
 {
-    resetPQExpBuffer(&conn->errorMessage);
     appendPQExpBuffer(&conn->errorMessage, "%s:", mprefix);
     pg_GSS_error_int(&conn->errorMessage, maj_stat, GSS_C_GSS_CODE);
     appendPQExpBufferChar(&conn->errorMessage, ':');
@@ -94,7 +93,7 @@ pg_GSS_load_servicename(PGconn *conn)
     host = PQhost(conn);
     if (!(host && host[0] != '\0'))
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("host name must be specified\n"));
         return STATUS_ERROR;
     }
@@ -107,7 +106,7 @@ pg_GSS_load_servicename(PGconn *conn)
     temp_gbuf.value = (char *) malloc(maxlen);
     if (!temp_gbuf.value)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("out of memory\n"));
         return STATUS_ERROR;
     }
diff --git a/src/interfaces/libpq/fe-lobj.c b/src/interfaces/libpq/fe-lobj.c
index 432935061f..5a6e4df3a0 100644
--- a/src/interfaces/libpq/fe-lobj.c
+++ b/src/interfaces/libpq/fe-lobj.c
@@ -61,11 +61,8 @@ lo_open(PGconn *conn, Oid lobjId, int mode)
     PQArgBlock    argv[2];
     PGresult   *res;

-    if (conn == NULL || conn->lobjfuncs == NULL)
-    {
-        if (lo_initialize(conn) < 0)
-            return -1;
-    }
+    if (lo_initialize(conn) < 0)
+        return -1;

     argv[0].isint = 1;
     argv[0].len = 4;
@@ -103,11 +100,8 @@ lo_close(PGconn *conn, int fd)
     int            retval;
     int            result_len;

-    if (conn == NULL || conn->lobjfuncs == NULL)
-    {
-        if (lo_initialize(conn) < 0)
-            return -1;
-    }
+    if (lo_initialize(conn) < 0)
+        return -1;

     argv[0].isint = 1;
     argv[0].len = 4;
@@ -141,16 +135,13 @@ lo_truncate(PGconn *conn, int fd, size_t len)
     int            retval;
     int            result_len;

-    if (conn == NULL || conn->lobjfuncs == NULL)
-    {
-        if (lo_initialize(conn) < 0)
-            return -1;
-    }
+    if (lo_initialize(conn) < 0)
+        return -1;

     /* Must check this on-the-fly because it's not there pre-8.3 */
     if (conn->lobjfuncs->fn_lo_truncate == 0)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("cannot determine OID of function lo_truncate\n"));
         return -1;
     }
@@ -166,7 +157,7 @@ lo_truncate(PGconn *conn, int fd, size_t len)
      */
     if (len > (size_t) INT_MAX)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("argument of lo_truncate exceeds integer range\n"));
         return -1;
     }
@@ -209,15 +200,12 @@ lo_truncate64(PGconn *conn, int fd, pg_int64 len)
     int            retval;
     int            result_len;

-    if (conn == NULL || conn->lobjfuncs == NULL)
-    {
-        if (lo_initialize(conn) < 0)
-            return -1;
-    }
+    if (lo_initialize(conn) < 0)
+        return -1;

     if (conn->lobjfuncs->fn_lo_truncate64 == 0)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("cannot determine OID of function lo_truncate64\n"));
         return -1;
     }
@@ -261,11 +249,8 @@ lo_read(PGconn *conn, int fd, char *buf, size_t len)
     PGresult   *res;
     int            result_len;

-    if (conn == NULL || conn->lobjfuncs == NULL)
-    {
-        if (lo_initialize(conn) < 0)
-            return -1;
-    }
+    if (lo_initialize(conn) < 0)
+        return -1;

     /*
      * Long ago, somebody thought it'd be a good idea to declare this function
@@ -275,7 +260,7 @@ lo_read(PGconn *conn, int fd, char *buf, size_t len)
      */
     if (len > (size_t) INT_MAX)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("argument of lo_read exceeds integer range\n"));
         return -1;
     }
@@ -316,11 +301,8 @@ lo_write(PGconn *conn, int fd, const char *buf, size_t len)
     int            result_len;
     int            retval;

-    if (conn == NULL || conn->lobjfuncs == NULL)
-    {
-        if (lo_initialize(conn) < 0)
-            return -1;
-    }
+    if (lo_initialize(conn) < 0)
+        return -1;

     /*
      * Long ago, somebody thought it'd be a good idea to declare this function
@@ -330,7 +312,7 @@ lo_write(PGconn *conn, int fd, const char *buf, size_t len)
      */
     if (len > (size_t) INT_MAX)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("argument of lo_write exceeds integer range\n"));
         return -1;
     }
@@ -369,11 +351,8 @@ lo_lseek(PGconn *conn, int fd, int offset, int whence)
     int            retval;
     int            result_len;

-    if (conn == NULL || conn->lobjfuncs == NULL)
-    {
-        if (lo_initialize(conn) < 0)
-            return -1;
-    }
+    if (lo_initialize(conn) < 0)
+        return -1;

     argv[0].isint = 1;
     argv[0].len = 4;
@@ -413,15 +392,12 @@ lo_lseek64(PGconn *conn, int fd, pg_int64 offset, int whence)
     pg_int64    retval;
     int            result_len;

-    if (conn == NULL || conn->lobjfuncs == NULL)
-    {
-        if (lo_initialize(conn) < 0)
-            return -1;
-    }
+    if (lo_initialize(conn) < 0)
+        return -1;

     if (conn->lobjfuncs->fn_lo_lseek64 == 0)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("cannot determine OID of function lo_lseek64\n"));
         return -1;
     }
@@ -469,11 +445,8 @@ lo_creat(PGconn *conn, int mode)
     int            retval;
     int            result_len;

-    if (conn == NULL || conn->lobjfuncs == NULL)
-    {
-        if (lo_initialize(conn) < 0)
-            return InvalidOid;
-    }
+    if (lo_initialize(conn) < 0)
+        return InvalidOid;

     argv[0].isint = 1;
     argv[0].len = 4;
@@ -508,16 +481,13 @@ lo_create(PGconn *conn, Oid lobjId)
     int            retval;
     int            result_len;

-    if (conn == NULL || conn->lobjfuncs == NULL)
-    {
-        if (lo_initialize(conn) < 0)
-            return InvalidOid;
-    }
+    if (lo_initialize(conn) < 0)
+        return InvalidOid;

     /* Must check this on-the-fly because it's not there pre-8.1 */
     if (conn->lobjfuncs->fn_lo_create == 0)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("cannot determine OID of function lo_create\n"));
         return InvalidOid;
     }
@@ -552,11 +522,8 @@ lo_tell(PGconn *conn, int fd)
     PGresult   *res;
     int            result_len;

-    if (conn == NULL || conn->lobjfuncs == NULL)
-    {
-        if (lo_initialize(conn) < 0)
-            return -1;
-    }
+    if (lo_initialize(conn) < 0)
+        return -1;

     argv[0].isint = 1;
     argv[0].len = 4;
@@ -588,15 +555,12 @@ lo_tell64(PGconn *conn, int fd)
     PGresult   *res;
     int            result_len;

-    if (conn == NULL || conn->lobjfuncs == NULL)
-    {
-        if (lo_initialize(conn) < 0)
-            return -1;
-    }
+    if (lo_initialize(conn) < 0)
+        return -1;

     if (conn->lobjfuncs->fn_lo_tell64 == 0)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("cannot determine OID of function lo_tell64\n"));
         return -1;
     }
@@ -632,11 +596,8 @@ lo_unlink(PGconn *conn, Oid lobjId)
     int            result_len;
     int            retval;

-    if (conn == NULL || conn->lobjfuncs == NULL)
-    {
-        if (lo_initialize(conn) < 0)
-            return -1;
-    }
+    if (lo_initialize(conn) < 0)
+        return -1;

     argv[0].isint = 1;
     argv[0].len = 4;
@@ -696,13 +657,19 @@ lo_import_internal(PGconn *conn, const char *filename, Oid oid)
     int            lobj;
     char        sebuf[PG_STRERROR_R_BUFLEN];

+    if (conn == NULL)
+        return InvalidOid;
+
+    /* Since this is the beginning of a query cycle, reset the error buffer */
+    resetPQExpBuffer(&conn->errorMessage);
+
     /*
      * open the file to be read in
      */
     fd = open(filename, O_RDONLY | PG_BINARY, 0666);
     if (fd < 0)
     {                            /* error */
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("could not open file \"%s\": %s\n"),
                           filename, strerror_r(errno, sebuf, sizeof(sebuf)));
         return InvalidOid;
@@ -757,6 +724,7 @@ lo_import_internal(PGconn *conn, const char *filename, Oid oid)

         (void) lo_close(conn, lobj);
         (void) close(fd);
+        /* deliberately overwrite any error from lo_close */
         printfPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("could not read from file \"%s\": %s\n"),
                           filename,
@@ -811,6 +779,7 @@ lo_export(PGconn *conn, Oid lobjId, const char *filename)
         int            save_errno = errno;

         (void) lo_close(conn, lobj);
+        /* deliberately overwrite any error from lo_close */
         printfPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("could not open file \"%s\": %s\n"),
                           filename,
@@ -831,6 +800,7 @@ lo_export(PGconn *conn, Oid lobjId, const char *filename)

             (void) lo_close(conn, lobj);
             (void) close(fd);
+            /* deliberately overwrite any error from lo_close */
             printfPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("could not write to file \"%s\": %s\n"),
                               filename,
@@ -855,7 +825,7 @@ lo_export(PGconn *conn, Oid lobjId, const char *filename)
     /* if we already failed, don't overwrite that msg with a close error */
     if (close(fd) != 0 && result >= 0)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("could not write to file \"%s\": %s\n"),
                           filename, strerror_r(errno, sebuf, sizeof(sebuf)));
         result = -1;
@@ -868,9 +838,11 @@ lo_export(PGconn *conn, Oid lobjId, const char *filename)
 /*
  * lo_initialize
  *
- * Initialize the large object interface for an existing connection.
- * We ask the backend about the functions OID's in pg_proc for all
- * functions that are required for large object operations.
+ * Initialize for a new large-object operation on an existing connection.
+ * Return 0 if OK, -1 on failure.
+ *
+ * If we haven't previously done so, we collect the function OIDs from
+ * pg_proc for all functions that are required for large object operations.
  */
 static int
 lo_initialize(PGconn *conn)
@@ -882,16 +854,25 @@ lo_initialize(PGconn *conn)
     const char *fname;
     Oid            foid;

-    if (!conn)
+    /* Nothing we can do with no connection */
+    if (conn == NULL)
         return -1;

+    /* Since this is the beginning of a query cycle, reset the error buffer */
+    resetPQExpBuffer(&conn->errorMessage);
+
+    /* Nothing else to do if we already collected info */
+    if (conn->lobjfuncs != NULL)
+        return 0;
+
     /*
-     * Allocate the structure to hold the functions OID's
+     * Allocate the structure to hold the function OIDs.  We don't store it
+     * into the PGconn until it's successfully filled.
      */
     lobjfuncs = (PGlobjfuncs *) malloc(sizeof(PGlobjfuncs));
     if (lobjfuncs == NULL)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("out of memory\n"));
         return -1;
     }
@@ -942,7 +923,7 @@ lo_initialize(PGconn *conn)
     {
         free(lobjfuncs);
         PQclear(res);
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("query to initialize large object functions did not return data\n"));
         return -1;
     }
@@ -991,56 +972,56 @@ lo_initialize(PGconn *conn)
      */
     if (lobjfuncs->fn_lo_open == 0)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("cannot determine OID of function lo_open\n"));
         free(lobjfuncs);
         return -1;
     }
     if (lobjfuncs->fn_lo_close == 0)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("cannot determine OID of function lo_close\n"));
         free(lobjfuncs);
         return -1;
     }
     if (lobjfuncs->fn_lo_creat == 0)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("cannot determine OID of function lo_creat\n"));
         free(lobjfuncs);
         return -1;
     }
     if (lobjfuncs->fn_lo_unlink == 0)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("cannot determine OID of function lo_unlink\n"));
         free(lobjfuncs);
         return -1;
     }
     if (lobjfuncs->fn_lo_lseek == 0)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("cannot determine OID of function lo_lseek\n"));
         free(lobjfuncs);
         return -1;
     }
     if (lobjfuncs->fn_lo_tell == 0)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("cannot determine OID of function lo_tell\n"));
         free(lobjfuncs);
         return -1;
     }
     if (lobjfuncs->fn_lo_read == 0)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("cannot determine OID of function loread\n"));
         free(lobjfuncs);
         return -1;
     }
     if (lobjfuncs->fn_lo_write == 0)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("cannot determine OID of function lowrite\n"));
         free(lobjfuncs);
         return -1;
diff --git a/src/interfaces/libpq/fe-misc.c b/src/interfaces/libpq/fe-misc.c
index 6094f048f3..9d57f21119 100644
--- a/src/interfaces/libpq/fe-misc.c
+++ b/src/interfaces/libpq/fe-misc.c
@@ -379,7 +379,7 @@ pqCheckOutBufferSpace(size_t bytes_needed, PGconn *conn)
     }

     /* realloc failed. Probably out of memory */
-    printfPQExpBuffer(&conn->errorMessage,
+    appendPQExpBuffer(&conn->errorMessage,
                       "cannot allocate memory for output buffer\n");
     return EOF;
 }
@@ -473,7 +473,7 @@ pqCheckInBufferSpace(size_t bytes_needed, PGconn *conn)
     }

     /* realloc failed. Probably out of memory */
-    printfPQExpBuffer(&conn->errorMessage,
+    appendPQExpBuffer(&conn->errorMessage,
                       "cannot allocate memory for input buffer\n");
     return EOF;
 }
@@ -619,7 +619,7 @@ pqReadData(PGconn *conn)

     if (conn->sock == PGINVALID_SOCKET)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("connection not open\n"));
         return -1;
     }
@@ -798,7 +798,7 @@ retry4:
      * means the connection has been closed.  Cope.
      */
 definitelyEOF:
-    printfPQExpBuffer(&conn->errorMessage,
+    appendPQExpBuffer(&conn->errorMessage,
                       libpq_gettext("server closed the connection unexpectedly\n"
                                     "\tThis probably means the server terminated abnormally\n"
                                     "\tbefore or while processing the request.\n"));
@@ -836,6 +836,7 @@ pqSendSome(PGconn *conn, int len)
 {
     char       *ptr = conn->outBuffer;
     int            remaining = conn->outCount;
+    int            oldmsglen = conn->errorMessage.len;
     int            result = 0;

     /*
@@ -862,13 +863,10 @@ pqSendSome(PGconn *conn, int len)

     if (conn->sock == PGINVALID_SOCKET)
     {
-        printfPQExpBuffer(&conn->errorMessage,
-                          libpq_gettext("connection not open\n"));
         conn->write_failed = true;
-        /* Transfer error message to conn->write_err_msg, if possible */
+        /* Insert error message into conn->write_err_msg, if possible */
         /* (strdup failure is OK, we'll cope later) */
-        conn->write_err_msg = strdup(conn->errorMessage.data);
-        resetPQExpBuffer(&conn->errorMessage);
+        conn->write_err_msg = strdup(libpq_gettext("connection not open\n"));
         /* Discard queued data; no chance it'll ever be sent */
         conn->outCount = 0;
         return 0;
@@ -915,14 +913,16 @@ pqSendSome(PGconn *conn, int len)
                      * Transfer error message to conn->write_err_msg, if
                      * possible (strdup failure is OK, we'll cope later).
                      *
-                     * Note: this assumes that pqsecure_write and its children
-                     * will overwrite not append to conn->errorMessage.  If
-                     * that's ever changed, we could remember the length of
-                     * conn->errorMessage at entry to this routine, and then
-                     * save and delete just what was appended.
+                     * We only want to transfer whatever has been appended to
+                     * conn->errorMessage since we entered this routine.
                      */
-                    conn->write_err_msg = strdup(conn->errorMessage.data);
-                    resetPQExpBuffer(&conn->errorMessage);
+                    if (!PQExpBufferBroken(&conn->errorMessage))
+                    {
+                        conn->write_err_msg = strdup(conn->errorMessage.data +
+                                                     oldmsglen);
+                        conn->errorMessage.len = oldmsglen;
+                        conn->errorMessage.data[oldmsglen] = '\0';
+                    }

                     /* Discard queued data; no chance it'll ever be sent */
                     conn->outCount = 0;
@@ -1056,7 +1056,7 @@ pqWaitTimed(int forRead, int forWrite, PGconn *conn, time_t finish_time)

     if (result == 0)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("timeout expired\n"));
         return 1;
     }
@@ -1101,7 +1101,7 @@ pqSocketCheck(PGconn *conn, int forRead, int forWrite, time_t end_time)
         return -1;
     if (conn->sock == PGINVALID_SOCKET)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("invalid socket\n"));
         return -1;
     }
@@ -1124,7 +1124,7 @@ pqSocketCheck(PGconn *conn, int forRead, int forWrite, time_t end_time)
     {
         char        sebuf[PG_STRERROR_R_BUFLEN];

-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("select() failed: %s\n"),
                           SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
     }
diff --git a/src/interfaces/libpq/fe-protocol2.c b/src/interfaces/libpq/fe-protocol2.c
index ad6587f924..fa0614a92b 100644
--- a/src/interfaces/libpq/fe-protocol2.c
+++ b/src/interfaces/libpq/fe-protocol2.c
@@ -83,7 +83,7 @@ pqSetenvPoll(PGconn *conn)
             return PGRES_POLLING_OK;

         default:
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("invalid setenv state %c, probably indicative of memory corruption\n"),
                               conn->setenv_state);
             goto error_return;
@@ -380,7 +380,7 @@ pqSetenvPoll(PGconn *conn)
                 }

             default:
-                printfPQExpBuffer(&conn->errorMessage,
+                appendPQExpBuffer(&conn->errorMessage,
                                   libpq_gettext("invalid state %c, "
                                                 "probably indicative of memory corruption\n"),
                                   conn->setenv_state);
@@ -493,7 +493,7 @@ pqParseInput2(PGconn *conn)
                                                            PGRES_COMMAND_OK);
                         if (!conn->result)
                         {
-                            printfPQExpBuffer(&conn->errorMessage,
+                            appendPQExpBuffer(&conn->errorMessage,
                                               libpq_gettext("out of memory"));
                             pqSaveErrorResult(conn);
                         }
@@ -528,7 +528,7 @@ pqParseInput2(PGconn *conn)
                                                            PGRES_EMPTY_QUERY);
                         if (!conn->result)
                         {
-                            printfPQExpBuffer(&conn->errorMessage,
+                            appendPQExpBuffer(&conn->errorMessage,
                                               libpq_gettext("out of memory"));
                             pqSaveErrorResult(conn);
                         }
@@ -622,7 +622,7 @@ pqParseInput2(PGconn *conn)
                      * never arrives from the server during protocol 2.0.
                      */
                 default:
-                    printfPQExpBuffer(&conn->errorMessage,
+                    appendPQExpBuffer(&conn->errorMessage,
                                       libpq_gettext("unexpected response from server; first received character was
\"%c\"\n"),
                                       id);
                     /* build an error result holding the error message */
@@ -754,7 +754,7 @@ advance_and_error:
     if (!errmsg)
         errmsg = libpq_gettext("out of memory for query result");

-    printfPQExpBuffer(&conn->errorMessage, "%s\n", errmsg);
+    appendPQExpBuffer(&conn->errorMessage, "%s\n", errmsg);

     /*
      * XXX: if PQmakeEmptyPGresult() fails, there's probably not much we can
@@ -929,7 +929,7 @@ set_error_result:
     if (!errmsg)
         errmsg = libpq_gettext("out of memory for query result");

-    printfPQExpBuffer(&conn->errorMessage, "%s\n", errmsg);
+    appendPQExpBuffer(&conn->errorMessage, "%s\n", errmsg);

     /*
      * XXX: if PQmakeEmptyPGresult() fails, there's probably not much we can
@@ -1042,11 +1042,10 @@ pqGetErrorNotice2(PGconn *conn, bool isError)
     {
         pqClearAsyncResult(conn);    /* redundant, but be safe */
         conn->result = res;
-        resetPQExpBuffer(&conn->errorMessage);
         if (res && !PQExpBufferDataBroken(workBuf) && res->errMsg)
             appendPQExpBufferStr(&conn->errorMessage, res->errMsg);
         else
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("out of memory"));
         if (conn->xactStatus == PQTRANS_INTRANS)
             conn->xactStatus = PQTRANS_INERROR;
@@ -1203,7 +1202,7 @@ pqGetCopyData2(PGconn *conn, char **buffer, int async)
         *buffer = (char *) malloc(msgLength + 1);
         if (*buffer == NULL)
         {
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("out of memory\n"));
             return -2;
         }
@@ -1349,7 +1348,7 @@ pqEndcopy2(PGconn *conn)
     if (conn->asyncStatus != PGASYNC_COPY_IN &&
         conn->asyncStatus != PGASYNC_COPY_OUT)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("no COPY in progress\n"));
         return 1;
     }
@@ -1367,7 +1366,6 @@ pqEndcopy2(PGconn *conn)

     /* Return to active duty */
     conn->asyncStatus = PGASYNC_BUSY;
-    resetPQExpBuffer(&conn->errorMessage);

     /* Wait for the completion response */
     result = PQgetResult(conn);
@@ -1526,7 +1524,7 @@ pqFunctionCall2(PGconn *conn, Oid fnid,
                 else
                 {
                     /* The backend violates the protocol. */
-                    printfPQExpBuffer(&conn->errorMessage,
+                    appendPQExpBuffer(&conn->errorMessage,
                                       libpq_gettext("protocol error: id=0x%x\n"),
                                       id);
                     pqSaveErrorResult(conn);
@@ -1558,7 +1556,7 @@ pqFunctionCall2(PGconn *conn, Oid fnid,
                 return PQmakeEmptyPGresult(conn, status);
             default:
                 /* The backend violates the protocol. */
-                printfPQExpBuffer(&conn->errorMessage,
+                appendPQExpBuffer(&conn->errorMessage,
                                   libpq_gettext("protocol error: id=0x%x\n"),
                                   id);
                 pqSaveErrorResult(conn);
diff --git a/src/interfaces/libpq/fe-protocol3.c b/src/interfaces/libpq/fe-protocol3.c
index a4d6ee2674..13cf7bc8c4 100644
--- a/src/interfaces/libpq/fe-protocol3.c
+++ b/src/interfaces/libpq/fe-protocol3.c
@@ -202,7 +202,7 @@ pqParseInput3(PGconn *conn)
                                                            PGRES_COMMAND_OK);
                         if (!conn->result)
                         {
-                            printfPQExpBuffer(&conn->errorMessage,
+                            appendPQExpBuffer(&conn->errorMessage,
                                               libpq_gettext("out of memory"));
                             pqSaveErrorResult(conn);
                         }
@@ -229,7 +229,7 @@ pqParseInput3(PGconn *conn)
                                                            PGRES_EMPTY_QUERY);
                         if (!conn->result)
                         {
-                            printfPQExpBuffer(&conn->errorMessage,
+                            appendPQExpBuffer(&conn->errorMessage,
                                               libpq_gettext("out of memory"));
                             pqSaveErrorResult(conn);
                         }
@@ -246,7 +246,7 @@ pqParseInput3(PGconn *conn)
                                                                PGRES_COMMAND_OK);
                             if (!conn->result)
                             {
-                                printfPQExpBuffer(&conn->errorMessage,
+                                appendPQExpBuffer(&conn->errorMessage,
                                                   libpq_gettext("out of memory"));
                                 pqSaveErrorResult(conn);
                             }
@@ -326,7 +326,7 @@ pqParseInput3(PGconn *conn)
                                                                PGRES_COMMAND_OK);
                             if (!conn->result)
                             {
-                                printfPQExpBuffer(&conn->errorMessage,
+                                appendPQExpBuffer(&conn->errorMessage,
                                                   libpq_gettext("out of memory"));
                                 pqSaveErrorResult(conn);
                             }
@@ -361,7 +361,7 @@ pqParseInput3(PGconn *conn)
                     else
                     {
                         /* Set up to report error at end of query */
-                        printfPQExpBuffer(&conn->errorMessage,
+                        appendPQExpBuffer(&conn->errorMessage,
                                           libpq_gettext("server sent data (\"D\" message) without prior row
description(\"T\" message)\n")); 
                         pqSaveErrorResult(conn);
                         /* Discard the unexpected message */
@@ -404,7 +404,7 @@ pqParseInput3(PGconn *conn)
                      */
                     break;
                 default:
-                    printfPQExpBuffer(&conn->errorMessage,
+                    appendPQExpBuffer(&conn->errorMessage,
                                       libpq_gettext("unexpected response from server; first received character was
\"%c\"\n"),
                                       id);
                     /* build an error result holding the error message */
@@ -425,7 +425,7 @@ pqParseInput3(PGconn *conn)
         else
         {
             /* Trouble --- report it */
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("message contents do not agree with length in message type \"%c\"\n"),
                               id);
             /* build an error result holding the error message */
@@ -445,7 +445,7 @@ pqParseInput3(PGconn *conn)
 static void
 handleSyncLoss(PGconn *conn, char id, int msgLength)
 {
-    printfPQExpBuffer(&conn->errorMessage,
+    appendPQExpBuffer(&conn->errorMessage,
                       libpq_gettext("lost synchronization with server: got message type \"%c\", length %d\n"),
                       id, msgLength);
     /* build an error result holding the error message */
@@ -621,7 +621,7 @@ advance_and_error:
     if (!errmsg)
         errmsg = libpq_gettext("out of memory for query result");

-    printfPQExpBuffer(&conn->errorMessage, "%s\n", errmsg);
+    appendPQExpBuffer(&conn->errorMessage, "%s\n", errmsg);
     pqSaveErrorResult(conn);

     /*
@@ -721,7 +721,7 @@ advance_and_error:
      */
     if (!errmsg)
         errmsg = libpq_gettext("out of memory");
-    printfPQExpBuffer(&conn->errorMessage, "%s\n", errmsg);
+    appendPQExpBuffer(&conn->errorMessage, "%s\n", errmsg);
     pqSaveErrorResult(conn);

     /*
@@ -848,7 +848,7 @@ set_error_result:
     if (!errmsg)
         errmsg = libpq_gettext("out of memory for query result");

-    printfPQExpBuffer(&conn->errorMessage, "%s\n", errmsg);
+    appendPQExpBuffer(&conn->errorMessage, "%s\n", errmsg);
     pqSaveErrorResult(conn);

     /*
@@ -950,7 +950,7 @@ pqGetErrorNotice3(PGconn *conn, bool isError)
         pqClearAsyncResult(conn);    /* redundant, but be safe */
         conn->result = res;
         if (PQExpBufferDataBroken(workBuf))
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("out of memory"));
         else
             appendPQExpBufferStr(&conn->errorMessage, workBuf.data);
@@ -1695,7 +1695,7 @@ pqGetCopyData3(PGconn *conn, char **buffer, int async)
             *buffer = (char *) malloc(msgLength + 1);
             if (*buffer == NULL)
             {
-                printfPQExpBuffer(&conn->errorMessage,
+                appendPQExpBuffer(&conn->errorMessage,
                                   libpq_gettext("out of memory\n"));
                 return -2;
             }
@@ -1728,7 +1728,7 @@ pqGetline3(PGconn *conn, char *s, int maxlen)
          conn->asyncStatus != PGASYNC_COPY_BOTH) ||
         conn->copy_is_binary)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("PQgetline: not doing text COPY OUT\n"));
         *s = '\0';
         return EOF;
@@ -1834,7 +1834,7 @@ pqEndcopy3(PGconn *conn)
         conn->asyncStatus != PGASYNC_COPY_OUT &&
         conn->asyncStatus != PGASYNC_COPY_BOTH)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("no COPY in progress\n"));
         return 1;
     }
@@ -1868,7 +1868,6 @@ pqEndcopy3(PGconn *conn)

     /* Return to active duty */
     conn->asyncStatus = PGASYNC_BUSY;
-    resetPQExpBuffer(&conn->errorMessage);

     /*
      * Non blocking connections may have to abort at this point.  If everyone
@@ -2091,7 +2090,7 @@ pqFunctionCall3(PGconn *conn, Oid fnid,
                 break;
             default:
                 /* The backend violates the protocol. */
-                printfPQExpBuffer(&conn->errorMessage,
+                appendPQExpBuffer(&conn->errorMessage,
                                   libpq_gettext("protocol error: id=0x%x\n"),
                                   id);
                 pqSaveErrorResult(conn);
diff --git a/src/interfaces/libpq/fe-secure-common.c b/src/interfaces/libpq/fe-secure-common.c
index 45d36359a5..44c69e1d19 100644
--- a/src/interfaces/libpq/fe-secure-common.c
+++ b/src/interfaces/libpq/fe-secure-common.c
@@ -94,7 +94,7 @@ pq_verify_peer_name_matches_certificate_name(PGconn *conn,

     if (!(host && host[0] != '\0'))
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("host name must be specified\n"));
         return -1;
     }
@@ -106,7 +106,7 @@ pq_verify_peer_name_matches_certificate_name(PGconn *conn,
     name = malloc(namelen + 1);
     if (name == NULL)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("out of memory\n"));
         return -1;
     }
@@ -120,7 +120,7 @@ pq_verify_peer_name_matches_certificate_name(PGconn *conn,
     if (namelen != strlen(name))
     {
         free(name);
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("SSL certificate's name contains embedded null\n"));
         return -1;
     }
@@ -167,7 +167,7 @@ pq_verify_peer_name_matches_certificate(PGconn *conn)
     /* Check that we have a hostname to compare with. */
     if (!(host && host[0] != '\0'))
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("host name must be specified for a verified SSL connection\n"));
         return false;
     }
@@ -184,7 +184,7 @@ pq_verify_peer_name_matches_certificate(PGconn *conn)
          */
         if (names_examined > 1)
         {
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_ngettext("server certificate for \"%s\" (and %d other name) does not match host
name\"%s\"\n", 
                                              "server certificate for \"%s\" (and %d other names) does not match host
name\"%s\"\n", 
                                              names_examined - 1),
@@ -192,13 +192,13 @@ pq_verify_peer_name_matches_certificate(PGconn *conn)
         }
         else if (names_examined == 1)
         {
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("server certificate for \"%s\" does not match host name \"%s\"\n"),
                               first_name, host);
         }
         else
         {
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("could not get server's host name from server certificate\n"));
         }
     }
diff --git a/src/interfaces/libpq/fe-secure-gssapi.c b/src/interfaces/libpq/fe-secure-gssapi.c
index 8c0ba69b7c..27e29a4779 100644
--- a/src/interfaces/libpq/fe-secure-gssapi.c
+++ b/src/interfaces/libpq/fe-secure-gssapi.c
@@ -78,7 +78,7 @@
  *
  * On success, returns the number of data bytes consumed (possibly less than
  * len).  On failure, returns -1 with errno set appropriately.  If the errno
- * indicates a non-retryable error, a message is put into conn->errorMessage.
+ * indicates a non-retryable error, a message is added to conn->errorMessage.
  * For retryable errors, caller should call again (passing the same data)
  * once the socket is ready.
  */
@@ -106,7 +106,7 @@ pg_GSS_write(PGconn *conn, const void *ptr, size_t len)
      */
     if (len < PqGSSSendConsumed)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           "GSSAPI caller failed to retransmit all data needing to be retried\n");
         errno = EINVAL;
         return -1;
@@ -205,7 +205,7 @@ pg_GSS_write(PGconn *conn, const void *ptr, size_t len)

         if (conf_state == 0)
         {
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("outgoing GSSAPI message would not use confidentiality\n"));
             errno = EIO;        /* for lack of a better idea */
             goto cleanup;
@@ -213,7 +213,7 @@ pg_GSS_write(PGconn *conn, const void *ptr, size_t len)

         if (output.length > PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32))
         {
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("client tried to send oversize GSSAPI packet (%zu > %zu)\n"),
                               (size_t) output.length,
                               PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32));
@@ -258,7 +258,7 @@ cleanup:
  *
  * Returns the number of data bytes read, or on failure, returns -1
  * with errno set appropriately.  If the errno indicates a non-retryable
- * error, a message is put into conn->errorMessage.  For retryable errors,
+ * error, a message is added to conn->errorMessage.  For retryable errors,
  * caller should call again once the socket is ready.
  */
 ssize_t
@@ -350,7 +350,7 @@ pg_GSS_read(PGconn *conn, void *ptr, size_t len)

         if (input.length > PQ_GSS_RECV_BUFFER_SIZE - sizeof(uint32))
         {
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("oversize GSSAPI packet sent by the server (%zu > %zu)\n"),
                               (size_t) input.length,
                               PQ_GSS_RECV_BUFFER_SIZE - sizeof(uint32));
@@ -399,7 +399,7 @@ pg_GSS_read(PGconn *conn, void *ptr, size_t len)

         if (conf_state == 0)
         {
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("incoming GSSAPI message did not use confidentiality\n"));
             ret = -1;
             errno = EIO;        /* for lack of a better idea */
@@ -500,7 +500,7 @@ pqsecure_open_gss(PGconn *conn)
         PqGSSResultBuffer = malloc(PQ_GSS_RECV_BUFFER_SIZE);
         if (!PqGSSSendBuffer || !PqGSSRecvBuffer || !PqGSSResultBuffer)
         {
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("out of memory\n"));
             return PGRES_POLLING_FAILED;
         }
@@ -578,7 +578,7 @@ pqsecure_open_gss(PGconn *conn)

             PqGSSRecvLength += ret;

-            printfPQExpBuffer(&conn->errorMessage, "%s\n", PqGSSRecvBuffer + 1);
+            appendPQExpBuffer(&conn->errorMessage, "%s\n", PqGSSRecvBuffer + 1);

             return PGRES_POLLING_FAILED;
         }
@@ -592,7 +592,7 @@ pqsecure_open_gss(PGconn *conn)
         input.length = pg_ntoh32(*(uint32 *) PqGSSRecvBuffer);
         if (input.length > PQ_GSS_RECV_BUFFER_SIZE - sizeof(uint32))
         {
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("oversize GSSAPI packet sent by the server (%zu > %zu)\n"),
                               (size_t) input.length,
                               PQ_GSS_RECV_BUFFER_SIZE - sizeof(uint32));
diff --git a/src/interfaces/libpq/fe-secure-openssl.c b/src/interfaces/libpq/fe-secure-openssl.c
index d63e4bb279..539e2053b7 100644
--- a/src/interfaces/libpq/fe-secure-openssl.c
+++ b/src/interfaces/libpq/fe-secure-openssl.c
@@ -181,7 +181,7 @@ rloop:
             if (n < 0)
             {
                 /* Not supposed to happen, so we don't translate the msg */
-                printfPQExpBuffer(&conn->errorMessage,
+                appendPQExpBuffer(&conn->errorMessage,
                                   "SSL_read failed but did not provide error information\n");
                 /* assume the connection is broken */
                 result_errno = ECONNRESET;
@@ -205,19 +205,19 @@ rloop:
                 result_errno = SOCK_ERRNO;
                 if (result_errno == EPIPE ||
                     result_errno == ECONNRESET)
-                    printfPQExpBuffer(&conn->errorMessage,
+                    appendPQExpBuffer(&conn->errorMessage,
                                       libpq_gettext("server closed the connection unexpectedly\n"
                                                     "\tThis probably means the server terminated abnormally\n"
                                                     "\tbefore or while processing the request.\n"));
                 else
-                    printfPQExpBuffer(&conn->errorMessage,
+                    appendPQExpBuffer(&conn->errorMessage,
                                       libpq_gettext("SSL SYSCALL error: %s\n"),
                                       SOCK_STRERROR(result_errno,
                                                     sebuf, sizeof(sebuf)));
             }
             else
             {
-                printfPQExpBuffer(&conn->errorMessage,
+                appendPQExpBuffer(&conn->errorMessage,
                                   libpq_gettext("SSL SYSCALL error: EOF detected\n"));
                 /* assume the connection is broken */
                 result_errno = ECONNRESET;
@@ -228,7 +228,7 @@ rloop:
             {
                 char       *errm = SSLerrmessage(ecode);

-                printfPQExpBuffer(&conn->errorMessage,
+                appendPQExpBuffer(&conn->errorMessage,
                                   libpq_gettext("SSL error: %s\n"), errm);
                 SSLerrfree(errm);
                 /* assume the connection is broken */
@@ -243,13 +243,13 @@ rloop:
              * a clean connection closure, so we should not report it as a
              * server crash.
              */
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("SSL connection has been closed unexpectedly\n"));
             result_errno = ECONNRESET;
             n = -1;
             break;
         default:
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("unrecognized SSL error code: %d\n"),
                               err);
             /* assume the connection is broken */
@@ -290,7 +290,7 @@ pgtls_write(PGconn *conn, const void *ptr, size_t len)
             if (n < 0)
             {
                 /* Not supposed to happen, so we don't translate the msg */
-                printfPQExpBuffer(&conn->errorMessage,
+                appendPQExpBuffer(&conn->errorMessage,
                                   "SSL_write failed but did not provide error information\n");
                 /* assume the connection is broken */
                 result_errno = ECONNRESET;
@@ -312,19 +312,19 @@ pgtls_write(PGconn *conn, const void *ptr, size_t len)
             {
                 result_errno = SOCK_ERRNO;
                 if (result_errno == EPIPE || result_errno == ECONNRESET)
-                    printfPQExpBuffer(&conn->errorMessage,
+                    appendPQExpBuffer(&conn->errorMessage,
                                       libpq_gettext("server closed the connection unexpectedly\n"
                                                     "\tThis probably means the server terminated abnormally\n"
                                                     "\tbefore or while processing the request.\n"));
                 else
-                    printfPQExpBuffer(&conn->errorMessage,
+                    appendPQExpBuffer(&conn->errorMessage,
                                       libpq_gettext("SSL SYSCALL error: %s\n"),
                                       SOCK_STRERROR(result_errno,
                                                     sebuf, sizeof(sebuf)));
             }
             else
             {
-                printfPQExpBuffer(&conn->errorMessage,
+                appendPQExpBuffer(&conn->errorMessage,
                                   libpq_gettext("SSL SYSCALL error: EOF detected\n"));
                 /* assume the connection is broken */
                 result_errno = ECONNRESET;
@@ -335,7 +335,7 @@ pgtls_write(PGconn *conn, const void *ptr, size_t len)
             {
                 char       *errm = SSLerrmessage(ecode);

-                printfPQExpBuffer(&conn->errorMessage,
+                appendPQExpBuffer(&conn->errorMessage,
                                   libpq_gettext("SSL error: %s\n"), errm);
                 SSLerrfree(errm);
                 /* assume the connection is broken */
@@ -350,13 +350,13 @@ pgtls_write(PGconn *conn, const void *ptr, size_t len)
              * a clean connection closure, so we should not report it as a
              * server crash.
              */
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("SSL connection has been closed unexpectedly\n"));
             result_errno = ECONNRESET;
             n = -1;
             break;
         default:
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("unrecognized SSL error code: %d\n"),
                               err);
             /* assume the connection is broken */
@@ -396,7 +396,7 @@ pgtls_get_peer_certificate_hash(PGconn *conn, size_t *len)
     if (!OBJ_find_sigid_algs(X509_get_signature_nid(peer_cert),
                              &algo_nid, NULL))
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("could not determine server certificate signature algorithm\n"));
         return NULL;
     }
@@ -417,7 +417,7 @@ pgtls_get_peer_certificate_hash(PGconn *conn, size_t *len)
             algo_type = EVP_get_digestbynid(algo_nid);
             if (algo_type == NULL)
             {
-                printfPQExpBuffer(&conn->errorMessage,
+                appendPQExpBuffer(&conn->errorMessage,
                                   libpq_gettext("could not find digest for NID %s\n"),
                                   OBJ_nid2sn(algo_nid));
                 return NULL;
@@ -427,7 +427,7 @@ pgtls_get_peer_certificate_hash(PGconn *conn, size_t *len)

     if (!X509_digest(peer_cert, algo_type, hash, &hash_size))
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("could not generate peer certificate hash\n"));
         return NULL;
     }
@@ -436,7 +436,7 @@ pgtls_get_peer_certificate_hash(PGconn *conn, size_t *len)
     cert_hash = malloc(hash_size);
     if (cert_hash == NULL)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("out of memory\n"));
         return NULL;
     }
@@ -484,7 +484,7 @@ openssl_verify_peer_name_matches_certificate_name(PGconn *conn, ASN1_STRING *nam
     /* Should not happen... */
     if (name_entry == NULL)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("SSL certificate's name entry is missing\n"));
         return -1;
     }
@@ -811,7 +811,7 @@ initialize_SSL(PGconn *conn)
     {
         char       *err = SSLerrmessage(ERR_get_error());

-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("could not create SSL context: %s\n"),
                           err);
         SSLerrfree(err);
@@ -850,7 +850,7 @@ initialize_SSL(PGconn *conn)

         if (ssl_min_ver == -1)
         {
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("invalid value \"%s\" for minimum SSL protocol version\n"),
                               conn->ssl_min_protocol_version);
             SSL_CTX_free(SSL_context);
@@ -861,7 +861,7 @@ initialize_SSL(PGconn *conn)
         {
             char       *err = SSLerrmessage(ERR_get_error());

-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("could not set minimum SSL protocol version: %s\n"),
                               err);
             SSLerrfree(err);
@@ -879,7 +879,7 @@ initialize_SSL(PGconn *conn)

         if (ssl_max_ver == -1)
         {
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("invalid value \"%s\" for maximum SSL protocol version\n"),
                               conn->ssl_max_protocol_version);
             SSL_CTX_free(SSL_context);
@@ -890,7 +890,7 @@ initialize_SSL(PGconn *conn)
         {
             char       *err = SSLerrmessage(ERR_get_error());

-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("could not set maximum SSL protocol version: %s\n"),
                               err);
             SSLerrfree(err);
@@ -926,7 +926,7 @@ initialize_SSL(PGconn *conn)
         {
             char       *err = SSLerrmessage(ERR_get_error());

-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("could not read root certificate file \"%s\": %s\n"),
                               fnbuf, err);
             SSLerrfree(err);
@@ -970,11 +970,11 @@ initialize_SSL(PGconn *conn)
              * that it seems worth having a specialized error message for it.
              */
             if (fnbuf[0] == '\0')
-                printfPQExpBuffer(&conn->errorMessage,
+                appendPQExpBuffer(&conn->errorMessage,
                                   libpq_gettext("could not get home directory to locate root certificate file\n"
                                                 "Either provide the file or change sslmode to disable server
certificateverification.\n")); 
             else
-                printfPQExpBuffer(&conn->errorMessage,
+                appendPQExpBuffer(&conn->errorMessage,
                                   libpq_gettext("root certificate file \"%s\" does not exist\n"
                                                 "Either provide the file or change sslmode to disable server
certificateverification.\n"), fnbuf); 
             SSL_CTX_free(SSL_context);
@@ -1005,7 +1005,7 @@ initialize_SSL(PGconn *conn)
          */
         if (errno != ENOENT && errno != ENOTDIR)
         {
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("could not open certificate file \"%s\": %s\n"),
                               fnbuf, strerror_r(errno, sebuf, sizeof(sebuf)));
             SSL_CTX_free(SSL_context);
@@ -1024,7 +1024,7 @@ initialize_SSL(PGconn *conn)
         {
             char       *err = SSLerrmessage(ERR_get_error());

-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("could not read certificate file \"%s\": %s\n"),
                               fnbuf, err);
             SSLerrfree(err);
@@ -1049,7 +1049,7 @@ initialize_SSL(PGconn *conn)
     {
         char       *err = SSLerrmessage(ERR_get_error());

-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("could not establish SSL connection: %s\n"),
                           err);
         SSLerrfree(err);
@@ -1087,7 +1087,7 @@ initialize_SSL(PGconn *conn)

             if (engine_str == NULL)
             {
-                printfPQExpBuffer(&conn->errorMessage,
+                appendPQExpBuffer(&conn->errorMessage,
                                   libpq_gettext("out of memory\n"));
                 return -1;
             }
@@ -1103,7 +1103,7 @@ initialize_SSL(PGconn *conn)
             {
                 char       *err = SSLerrmessage(ERR_get_error());

-                printfPQExpBuffer(&conn->errorMessage,
+                appendPQExpBuffer(&conn->errorMessage,
                                   libpq_gettext("could not load SSL engine \"%s\": %s\n"),
                                   engine_str, err);
                 SSLerrfree(err);
@@ -1115,7 +1115,7 @@ initialize_SSL(PGconn *conn)
             {
                 char       *err = SSLerrmessage(ERR_get_error());

-                printfPQExpBuffer(&conn->errorMessage,
+                appendPQExpBuffer(&conn->errorMessage,
                                   libpq_gettext("could not initialize SSL engine \"%s\": %s\n"),
                                   engine_str, err);
                 SSLerrfree(err);
@@ -1131,7 +1131,7 @@ initialize_SSL(PGconn *conn)
             {
                 char       *err = SSLerrmessage(ERR_get_error());

-                printfPQExpBuffer(&conn->errorMessage,
+                appendPQExpBuffer(&conn->errorMessage,
                                   libpq_gettext("could not read private SSL key \"%s\" from engine \"%s\": %s\n"),
                                   engine_colon, engine_str, err);
                 SSLerrfree(err);
@@ -1145,7 +1145,7 @@ initialize_SSL(PGconn *conn)
             {
                 char       *err = SSLerrmessage(ERR_get_error());

-                printfPQExpBuffer(&conn->errorMessage,
+                appendPQExpBuffer(&conn->errorMessage,
                                   libpq_gettext("could not load private SSL key \"%s\" from engine \"%s\": %s\n"),
                                   engine_colon, engine_str, err);
                 SSLerrfree(err);
@@ -1182,7 +1182,7 @@ initialize_SSL(PGconn *conn)

         if (stat(fnbuf, &buf) != 0)
         {
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("certificate present, but not private key file \"%s\"\n"),
                               fnbuf);
             return -1;
@@ -1190,7 +1190,7 @@ initialize_SSL(PGconn *conn)
 #ifndef WIN32
         if (!S_ISREG(buf.st_mode) || buf.st_mode & (S_IRWXG | S_IRWXO))
         {
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("private key file \"%s\" has group or world access; permissions should be
u=rw(0600) or less\n"), 
                               fnbuf);
             return -1;
@@ -1215,7 +1215,7 @@ initialize_SSL(PGconn *conn)
              */
             if (SSL_use_PrivateKey_file(conn->ssl, fnbuf, SSL_FILETYPE_ASN1) != 1)
             {
-                printfPQExpBuffer(&conn->errorMessage,
+                appendPQExpBuffer(&conn->errorMessage,
                                   libpq_gettext("could not load private key file \"%s\": %s\n"),
                                   fnbuf, err);
                 SSLerrfree(err);
@@ -1233,7 +1233,7 @@ initialize_SSL(PGconn *conn)
     {
         char       *err = SSLerrmessage(ERR_get_error());

-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("certificate does not match private key file \"%s\": %s\n"),
                           fnbuf, err);
         SSLerrfree(err);
@@ -1287,11 +1287,11 @@ open_client_SSL(PGconn *conn)
                     char        sebuf[PG_STRERROR_R_BUFLEN];

                     if (r == -1)
-                        printfPQExpBuffer(&conn->errorMessage,
+                        appendPQExpBuffer(&conn->errorMessage,
                                           libpq_gettext("SSL SYSCALL error: %s\n"),
                                           SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
                     else
-                        printfPQExpBuffer(&conn->errorMessage,
+                        appendPQExpBuffer(&conn->errorMessage,
                                           libpq_gettext("SSL SYSCALL error: EOF detected\n"));
                     pgtls_close(conn);
                     return PGRES_POLLING_FAILED;
@@ -1300,7 +1300,7 @@ open_client_SSL(PGconn *conn)
                 {
                     char       *err = SSLerrmessage(ecode);

-                    printfPQExpBuffer(&conn->errorMessage,
+                    appendPQExpBuffer(&conn->errorMessage,
                                       libpq_gettext("SSL error: %s\n"),
                                       err);
                     SSLerrfree(err);
@@ -1350,7 +1350,7 @@ open_client_SSL(PGconn *conn)
                 }

             default:
-                printfPQExpBuffer(&conn->errorMessage,
+                appendPQExpBuffer(&conn->errorMessage,
                                   libpq_gettext("unrecognized SSL error code: %d\n"),
                                   err);
                 pgtls_close(conn);
@@ -1369,7 +1369,7 @@ open_client_SSL(PGconn *conn)
     {
         char       *err = SSLerrmessage(ERR_get_error());

-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("certificate could not be obtained: %s\n"),
                           err);
         SSLerrfree(err);
diff --git a/src/interfaces/libpq/fe-secure.c b/src/interfaces/libpq/fe-secure.c
index 373c59cb0d..15a37fad30 100644
--- a/src/interfaces/libpq/fe-secure.c
+++ b/src/interfaces/libpq/fe-secure.c
@@ -205,8 +205,8 @@ pqsecure_close(PGconn *conn)
 /*
  *    Read data from a secure connection.
  *
- * On failure, this function is responsible for putting a suitable message
- * into conn->errorMessage.  The caller must still inspect errno, but only
+ * On failure, this function is responsible for appending a suitable message
+ * to conn->errorMessage.  The caller must still inspect errno, but only
  * to determine whether to continue/retry after error.
  */
 ssize_t
@@ -263,14 +263,14 @@ pqsecure_raw_read(PGconn *conn, void *ptr, size_t len)

             case EPIPE:
             case ECONNRESET:
-                printfPQExpBuffer(&conn->errorMessage,
+                appendPQExpBuffer(&conn->errorMessage,
                                   libpq_gettext("server closed the connection unexpectedly\n"
                                                 "\tThis probably means the server terminated abnormally\n"
                                                 "\tbefore or while processing the request.\n"));
                 break;

             default:
-                printfPQExpBuffer(&conn->errorMessage,
+                appendPQExpBuffer(&conn->errorMessage,
                                   libpq_gettext("could not receive data from server: %s\n"),
                                   SOCK_STRERROR(result_errno,
                                                 sebuf, sizeof(sebuf)));
@@ -287,8 +287,8 @@ pqsecure_raw_read(PGconn *conn, void *ptr, size_t len)
 /*
  *    Write data to a secure connection.
  *
- * On failure, this function is responsible for putting a suitable message
- * into conn->errorMessage.  The caller must still inspect errno, but only
+ * On failure, this function is responsible for appending a suitable message
+ * to conn->errorMessage.  The caller must still inspect errno, but only
  * to determine whether to continue/retry after error.
  */
 ssize_t
@@ -376,14 +376,14 @@ retry_masked:
                 /* FALL THRU */

             case ECONNRESET:
-                printfPQExpBuffer(&conn->errorMessage,
+                appendPQExpBuffer(&conn->errorMessage,
                                   libpq_gettext("server closed the connection unexpectedly\n"
                                                 "\tThis probably means the server terminated abnormally\n"
                                                 "\tbefore or while processing the request.\n"));
                 break;

             default:
-                printfPQExpBuffer(&conn->errorMessage,
+                appendPQExpBuffer(&conn->errorMessage,
                                   libpq_gettext("could not send data to server: %s\n"),
                                   SOCK_STRERROR(result_errno,
                                                 sebuf, sizeof(sebuf)));
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index e1018adb9e..4db498369c 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -522,7 +522,11 @@ struct pg_conn
                                  * connection */
 #endif

-    /* Buffer for current error message */
+    /*
+     * Buffer for current error message.  This is cleared at the start of any
+     * connection attempt or query cycle; after that, all code should append
+     * messages to it, never overwrite.
+     */
     PQExpBufferData errorMessage;    /* expansible string */

     /* Buffer for receiving various parts of messages */
@@ -600,7 +604,6 @@ 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 *pqResultAlloc(PGresult *res, size_t nBytes, bool isBinary);
 extern char *pqResultStrdup(PGresult *res, const char *str);
 extern void pqClearAsyncResult(PGconn *conn);
@@ -612,6 +615,7 @@ extern void pqSaveMessageField(PGresult *res, char code,
 extern void pqSaveParameterStatus(PGconn *conn, const char *name,
                                   const char *value);
 extern int    pqRowProcessor(PGconn *conn, const char **errmsgp);
+extern int    PQsendQueryContinue(PGconn *conn, const char *query);

 /* === in fe-protocol2.c === */

@@ -708,7 +712,7 @@ extern void pgtls_init_library(bool do_ssl, int do_crypto);
  * The conn parameter is only used to be able to pass back an error
  * message - no connection-local setup is made here.
  *
- * Returns 0 if OK, -1 on failure (with a message in conn->errorMessage).
+ * Returns 0 if OK, -1 on failure (adding a message to conn->errorMessage).
  */
 extern int    pgtls_init(PGconn *conn);

@@ -725,8 +729,8 @@ extern void pgtls_close(PGconn *conn);
 /*
  *    Read data from a secure connection.
  *
- * On failure, this function is responsible for putting a suitable message
- * into conn->errorMessage.  The caller must still inspect errno, but only
+ * On failure, this function is responsible for appending a suitable message
+ * to conn->errorMessage.  The caller must still inspect errno, but only
  * to determine whether to continue/retry after error.
  */
 extern ssize_t pgtls_read(PGconn *conn, void *ptr, size_t len);
@@ -739,8 +743,8 @@ extern bool pgtls_read_pending(PGconn *conn);
 /*
  *    Write data to a secure connection.
  *
- * On failure, this function is responsible for putting a suitable message
- * into conn->errorMessage.  The caller must still inspect errno, but only
+ * On failure, this function is responsible for appending a suitable message
+ * to conn->errorMessage.  The caller must still inspect errno, but only
  * to determine whether to continue/retry after error.
  */
 extern ssize_t pgtls_write(PGconn *conn, const void *ptr, size_t len);
diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl
index 11dc98ee0a..2b501166b8 100644
--- a/src/bin/pg_dump/t/002_pg_dump.pl
+++ b/src/bin/pg_dump/t/002_pg_dump.pl
@@ -3460,7 +3460,7 @@ foreach my $db (sort keys %create_sql)

 command_fails_like(
     [ 'pg_dump', '-p', "$port", 'qqq' ],
-    qr/\Qpg_dump: error: connection to database "qqq" failed: FATAL:  database "qqq" does not exist\E/,
+    qr/pg_dump: error: connection to database "qqq" failed: could not connect to .*: FATAL:  database "qqq" does not
exist/,
     'connecting to a non-existent database');

 #########################################
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 84e2868104..5b0770d8cc 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -1669,15 +1669,17 @@ getHostaddr(PGconn *conn, char *host_addr, int host_addr_len)
 }

 /* ----------
- * connectFailureMessage -
- * create a friendly error message on connection failure.
+ * emitCouldNotConnect -
+ * Speculatively append "could not connect to ...: " to conn->errorMessage
+ * once we've identified the current connection target address.  This ensures
+ * that any subsequent error message will be properly attributed to the
+ * server we couldn't connect to.  conn->raddr must be valid, and the result
+ * of getHostaddr() must be supplied.
  * ----------
  */
 static void
-connectFailureMessage(PGconn *conn, int errorno)
+emitCouldNotConnect(PGconn *conn, const char *host_addr)
 {
-    char        sebuf[PG_STRERROR_R_BUFLEN];
-
 #ifdef HAVE_UNIX_SOCKETS
     if (IS_AF_UNIX(conn->raddr.addr.ss_family))
     {
@@ -1688,25 +1690,15 @@ connectFailureMessage(PGconn *conn, int errorno)
                            service, sizeof(service),
                            NI_NUMERICSERV);
         appendPQExpBuffer(&conn->errorMessage,
-                          libpq_gettext("could not connect to server: %s\n"
-                                        "\tIs the server running locally and accepting\n"
-                                        "\tconnections on Unix domain socket \"%s\"?\n"),
-                          SOCK_STRERROR(errorno, sebuf, sizeof(sebuf)),
+                          libpq_gettext("could not connect to socket \"%s\": "),
                           service);
     }
     else
 #endif                            /* HAVE_UNIX_SOCKETS */
     {
-        char        host_addr[NI_MAXHOST];
         const char *displayed_host;
         const char *displayed_port;

-        /*
-         * Optionally display the network address with the hostname. This is
-         * useful to distinguish between IPv4 and IPv6 connections.
-         */
-        getHostaddr(conn, host_addr, NI_MAXHOST);
-
         /* To which host and port were we actually connecting? */
         if (conn->connhost[conn->whichhost].type == CHT_HOST_ADDRESS)
             displayed_host = conn->connhost[conn->whichhost].hostaddr;
@@ -1722,26 +1714,46 @@ connectFailureMessage(PGconn *conn, int errorno)
          * looked-up IP address.
          */
         if (conn->connhost[conn->whichhost].type != CHT_HOST_ADDRESS &&
-            strlen(host_addr) > 0 &&
+            host_addr[0] &&
             strcmp(displayed_host, host_addr) != 0)
             appendPQExpBuffer(&conn->errorMessage,
-                              libpq_gettext("could not connect to server: %s\n"
-                                            "\tIs the server running on host \"%s\" (%s) and accepting\n"
-                                            "\tTCP/IP connections on port %s?\n"),
-                              SOCK_STRERROR(errorno, sebuf, sizeof(sebuf)),
+                              libpq_gettext("could not connect to host \"%s\" (%s), port %s: "),
                               displayed_host, host_addr,
                               displayed_port);
         else
             appendPQExpBuffer(&conn->errorMessage,
-                              libpq_gettext("could not connect to server: %s\n"
-                                            "\tIs the server running on host \"%s\" and accepting\n"
-                                            "\tTCP/IP connections on port %s?\n"),
-                              SOCK_STRERROR(errorno, sebuf, sizeof(sebuf)),
+                              libpq_gettext("could not connect to host \"%s\", port %s: "),
                               displayed_host,
                               displayed_port);
     }
 }

+/* ----------
+ * connectFailureMessage -
+ * create a friendly error message on connection failure,
+ * using the given errno value.  Use this for error cases that
+ * imply that there's no server there.
+ * ----------
+ */
+static void
+connectFailureMessage(PGconn *conn, int errorno)
+{
+    char        sebuf[PG_STRERROR_R_BUFLEN];
+
+    appendPQExpBuffer(&conn->errorMessage,
+                      "%s\n",
+                      SOCK_STRERROR(errorno, sebuf, sizeof(sebuf)));
+
+#ifdef HAVE_UNIX_SOCKETS
+    if (IS_AF_UNIX(conn->raddr.addr.ss_family))
+        appendPQExpBufferStr(&conn->errorMessage,
+                             libpq_gettext("\tIs the server running locally and accepting connections on that
socket?\n"));
+    else
+#endif
+        appendPQExpBufferStr(&conn->errorMessage,
+                             libpq_gettext("\tIs the server running on that host and accepting TCP/IP
connections?\n"));
+}
+
 /*
  * Should we use keepalives?  Returns 1 if yes, 0 if no, and -1 if
  * conn->keepalives is set to a value which is not parseable as an
@@ -2476,30 +2488,30 @@ keep_going:                        /* We will come back to here until there is
                         goto keep_going;
                     }

-                    /* Remember current address for possible error msg */
+                    /* Remember current address for possible use later */
                     memcpy(&conn->raddr.addr, addr_cur->ai_addr,
                            addr_cur->ai_addrlen);
                     conn->raddr.salen = addr_cur->ai_addrlen;

-                    /* set connip */
+                    /*
+                     * Set connip, too.  Note we purposely ignore strdup
+                     * failure; not a big problem if it fails.
+                     */
                     if (conn->connip != NULL)
                     {
                         free(conn->connip);
                         conn->connip = NULL;
                     }
-
                     getHostaddr(conn, host_addr, NI_MAXHOST);
-                    if (strlen(host_addr) > 0)
+                    if (host_addr[0])
                         conn->connip = strdup(host_addr);

-                    /*
-                     * purposely ignore strdup failure; not a big problem if
-                     * it fails anyway.
-                     */
-
+                    /* Try to create the socket */
                     conn->sock = socket(addr_cur->ai_family, SOCK_STREAM, 0);
                     if (conn->sock == PGINVALID_SOCKET)
                     {
+                        int            errorno = SOCK_ERRNO;
+
                         /*
                          * Silently ignore socket() failure if we have more
                          * addresses to try; this reduces useless chatter in
@@ -2512,12 +2524,20 @@ keep_going:                        /* We will come back to here until there is
                             conn->try_next_addr = true;
                             goto keep_going;
                         }
+                        emitCouldNotConnect(conn, host_addr);
                         appendPQExpBuffer(&conn->errorMessage,
                                           libpq_gettext("could not create socket: %s\n"),
-                                          SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
+                                          SOCK_STRERROR(errorno, sebuf, sizeof(sebuf)));
                         goto error_return;
                     }

+                    /*
+                     * Once we've identified a target address, all errors
+                     * except the preceding socket()-failure case should be
+                     * prefixed with "could not connect to <target>: ".
+                     */
+                    emitCouldNotConnect(conn, host_addr);
+
                     /*
                      * Select socket options: no delay of outgoing data for
                      * TCP sockets, nonblock mode, close-on-exec.  Try the
@@ -3608,9 +3628,6 @@ keep_going:                        /* We will come back to here until there is
             }
         case CONNECTION_CHECK_WRITABLE:
             {
-                const char *displayed_host;
-                const char *displayed_port;
-
                 conn->status = CONNECTION_OK;
                 if (!PQconsumeInput(conn))
                     goto error_return;
@@ -3634,19 +3651,8 @@ keep_going:                        /* We will come back to here until there is
                         PQclear(res);

                         /* Append error report to conn->errorMessage. */
-                        if (conn->connhost[conn->whichhost].type == CHT_HOST_ADDRESS)
-                            displayed_host = conn->connhost[conn->whichhost].hostaddr;
-                        else
-                            displayed_host = conn->connhost[conn->whichhost].host;
-                        displayed_port = conn->connhost[conn->whichhost].port;
-                        if (displayed_port == NULL || displayed_port[0] == '\0')
-                            displayed_port = DEF_PGPORT_STR;
-
-                        appendPQExpBuffer(&conn->errorMessage,
-                                          libpq_gettext("could not make a writable "
-                                                        "connection to server "
-                                                        "\"%s:%s\"\n"),
-                                          displayed_host, displayed_port);
+                        appendPQExpBufferStr(&conn->errorMessage,
+                                             libpq_gettext("session is read-only\n"));

                         /* Close connection politely. */
                         conn->status = CONNECTION_OK;
@@ -3679,17 +3685,8 @@ keep_going:                        /* We will come back to here until there is
                     PQclear(res);

                 /* Append error report to conn->errorMessage. */
-                if (conn->connhost[conn->whichhost].type == CHT_HOST_ADDRESS)
-                    displayed_host = conn->connhost[conn->whichhost].hostaddr;
-                else
-                    displayed_host = conn->connhost[conn->whichhost].host;
-                displayed_port = conn->connhost[conn->whichhost].port;
-                if (displayed_port == NULL || displayed_port[0] == '\0')
-                    displayed_port = DEF_PGPORT_STR;
-                appendPQExpBuffer(&conn->errorMessage,
-                                  libpq_gettext("test \"SHOW transaction_read_only\" failed "
-                                                "on server \"%s:%s\"\n"),
-                                  displayed_host, displayed_port);
+                appendPQExpBufferStr(&conn->errorMessage,
+                                     libpq_gettext("test \"SHOW transaction_read_only\" failed\n"));

                 /* Close connection politely. */
                 conn->status = CONNECTION_OK;
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 5b0770d8cc..8391e2c46a 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -3315,6 +3315,20 @@ keep_going:                        /* We will come back to here until there is
                     /* OK, we read the message; mark data consumed */
                     conn->inStart = conn->inCursor;

+                    /*
+                     * If error is "cannot connect now", try the next host if
+                     * any (but we don't want to consider additional addresses
+                     * for this host, nor is there much point in changing SSL
+                     * or GSS mode).  This is helpful when dealing with
+                     * standby servers that might not be in hot-standby state.
+                     */
+                    if (strcmp(conn->last_sqlstate,
+                               ERRCODE_CANNOT_CONNECT_NOW) == 0)
+                    {
+                        conn->try_next_host = true;
+                        goto keep_going;
+                    }
+
                     /* Check to see if we should mention pgpassfile */
                     pgpassfileWarning(conn);

diff --git a/src/interfaces/ecpg/test/expected/connect-test5.stderr
b/src/interfaces/ecpg/test/expected/connect-test5.stderr
index a54df175fb..4dbf2c0fc4 100644
--- a/src/interfaces/ecpg/test/expected/connect-test5.stderr
+++ b/src/interfaces/ecpg/test/expected/connect-test5.stderr
@@ -36,7 +36,7 @@
 [NO_PID]: sqlca: code: 0, state: 00000
 [NO_PID]: ECPGconnect: opening database <DEFAULT> on <DEFAULT> port <DEFAULT>  for user regress_ecpg_user2
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ECPGconnect: could not open database: FATAL:  database "regress_ecpg_user2" does not exist
+[NO_PID]: ECPGconnect: could not open database: could not connect: FATAL:  database "regress_ecpg_user2" does not
exist

 [NO_PID]: sqlca: code: 0, state: 00000
 [NO_PID]: ecpg_finish: connection main closed
@@ -73,7 +73,7 @@
 [NO_PID]: sqlca: code: -220, state: 08003
 [NO_PID]: ECPGconnect: opening database <DEFAULT> on <DEFAULT> port <DEFAULT>  for user regress_ecpg_user2
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ECPGconnect: could not open database: FATAL:  database "regress_ecpg_user2" does not exist
+[NO_PID]: ECPGconnect: could not open database: could not connect: FATAL:  database "regress_ecpg_user2" does not
exist

 [NO_PID]: sqlca: code: 0, state: 00000
 [NO_PID]: ecpg_finish: connection main closed
diff --git a/src/interfaces/ecpg/test/pg_regress_ecpg.c b/src/interfaces/ecpg/test/pg_regress_ecpg.c
index 31dd507ea0..e3e0479ce3 100644
--- a/src/interfaces/ecpg/test/pg_regress_ecpg.c
+++ b/src/interfaces/ecpg/test/pg_regress_ecpg.c
@@ -23,13 +23,14 @@
 #include "lib/stringinfo.h"


+/*
+ * Create a filtered copy of sourcefile, removing any path
+ * appearing in #line directives; for example, replace
+ * #line x "./../bla/foo.h" with #line x "foo.h"
+ */
 static void
-ecpg_filter(const char *sourcefile, const char *outfile)
+ecpg_filter_source(const char *sourcefile, const char *outfile)
 {
-    /*
-     * Create a filtered copy of sourcefile, replacing #line x
-     * "./../bla/foo.h" with #line x "foo.h"
-     */
     FILE       *s,
                *t;
     StringInfoData linebuf;
@@ -76,6 +77,66 @@ ecpg_filter(const char *sourcefile, const char *outfile)
     fclose(t);
 }

+/*
+ * Remove the details of "could not connect to ...:" error messages
+ * in a test result file, since the target socket path and/or port
+ * can vary.  Rewrite the result file in-place.
+ *
+ * At some point it might be interesting to unify this with
+ * ecpg_filter_source, but building a general pattern matcher
+ * is no fun, nor does it seem desirable to introduce a
+ * dependency on an external one.
+ */
+static void
+ecpg_filter_stderr(const char *resultfile, const char *tmpfile)
+{
+    FILE       *s,
+               *t;
+    StringInfoData linebuf;
+
+    s = fopen(resultfile, "r");
+    if (!s)
+    {
+        fprintf(stderr, "Could not open file %s for reading\n", resultfile);
+        exit(2);
+    }
+    t = fopen(tmpfile, "w");
+    if (!t)
+    {
+        fprintf(stderr, "Could not open file %s for writing\n", tmpfile);
+        exit(2);
+    }
+
+    initStringInfo(&linebuf);
+
+    while (pg_get_line_buf(s, &linebuf))
+    {
+        char       *p1 = strstr(linebuf.data, "could not connect to ");
+
+        if (p1)
+        {
+            char       *p2 = strchr(p1, ':');
+
+            if (p2)
+            {
+                memmove(p1 + 17, p2, strlen(p2) + 1);
+                /* we don't bother to fix up linebuf.len */
+            }
+        }
+        fputs(linebuf.data, t);
+    }
+
+    pfree(linebuf.data);
+    fclose(s);
+    fclose(t);
+    if (rename(tmpfile, resultfile) != 0)
+    {
+        fprintf(stderr, "Could not overwrite file %s with %s\n",
+                resultfile, tmpfile);
+        exit(2);
+    }
+}
+
 /*
  * start an ecpg test process for specified file (including redirection),
  * and return process ID
@@ -139,7 +200,7 @@ ecpg_start_test(const char *testname,
     add_stringlist_item(expectfiles, expectfile_source);
     add_stringlist_item(tags, "source");

-    ecpg_filter(insource, outfile_source);
+    ecpg_filter_source(insource, outfile_source);

     snprintf(cmd, sizeof(cmd),
              "\"%s\" >\"%s\" 2>\"%s\"",
@@ -167,6 +228,21 @@ ecpg_start_test(const char *testname,
     return pid;
 }

+static void
+ecpg_postprocess_result(const char *filename)
+{
+    int            nlen = strlen(filename);
+
+    /* Only stderr files require filtering, at the moment */
+    if (nlen > 7 && strcmp(filename + nlen - 7, ".stderr") == 0)
+    {
+        char       *tmpfile = psprintf("%s.tmp", filename);
+
+        ecpg_filter_stderr(filename, tmpfile);
+        pfree(tmpfile);
+    }
+}
+
 static void
 ecpg_init(int argc, char *argv[])
 {
@@ -176,5 +252,8 @@ ecpg_init(int argc, char *argv[])
 int
 main(int argc, char *argv[])
 {
-    return regression_main(argc, argv, ecpg_init, ecpg_start_test);
+    return regression_main(argc, argv,
+                           ecpg_init,
+                           ecpg_start_test,
+                           ecpg_postprocess_result);
 }
diff --git a/src/test/isolation/isolation_main.c b/src/test/isolation/isolation_main.c
index 50893aa3ab..eff9c4f2df 100644
--- a/src/test/isolation/isolation_main.c
+++ b/src/test/isolation/isolation_main.c
@@ -145,5 +145,8 @@ isolation_init(int argc, char **argv)
 int
 main(int argc, char *argv[])
 {
-    return regression_main(argc, argv, isolation_init, isolation_start_test);
+    return regression_main(argc, argv,
+                           isolation_init,
+                           isolation_start_test,
+                           NULL /* no postfunc needed */ );
 }
diff --git a/src/test/regress/pg_regress.c b/src/test/regress/pg_regress.c
index 5cfb4c4a49..b284cc88c4 100644
--- a/src/test/regress/pg_regress.c
+++ b/src/test/regress/pg_regress.c
@@ -731,7 +731,7 @@ static void
 initialize_environment(void)
 {
     /*
-     * Set default application_name.  (The test_function may choose to
+     * Set default application_name.  (The test_start_function may choose to
      * override this, but if it doesn't, we have something useful in place.)
      */
     setenv("PGAPPNAME", "pg_regress", 1);
@@ -1616,7 +1616,8 @@ log_child_failure(int exitstatus)
  * Run all the tests specified in one schedule file
  */
 static void
-run_schedule(const char *schedule, test_function tfunc)
+run_schedule(const char *schedule, test_start_function startfunc,
+             postprocess_result_function postfunc)
 {
 #define MAX_PARALLEL_TESTS 100
     char       *tests[MAX_PARALLEL_TESTS];
@@ -1730,7 +1731,7 @@ run_schedule(const char *schedule, test_function tfunc)
         if (num_tests == 1)
         {
             status(_("test %-28s ... "), tests[0]);
-            pids[0] = (tfunc) (tests[0], &resultfiles[0], &expectfiles[0], &tags[0]);
+            pids[0] = (startfunc) (tests[0], &resultfiles[0], &expectfiles[0], &tags[0]);
             INSTR_TIME_SET_CURRENT(starttimes[0]);
             wait_for_tests(pids, statuses, stoptimes, NULL, 1);
             /* status line is finished below */
@@ -1756,7 +1757,7 @@ run_schedule(const char *schedule, test_function tfunc)
                                    tests + oldest, i - oldest);
                     oldest = i;
                 }
-                pids[i] = (tfunc) (tests[i], &resultfiles[i], &expectfiles[i], &tags[i]);
+                pids[i] = (startfunc) (tests[i], &resultfiles[i], &expectfiles[i], &tags[i]);
                 INSTR_TIME_SET_CURRENT(starttimes[i]);
             }
             wait_for_tests(pids + oldest, statuses + oldest,
@@ -1769,7 +1770,7 @@ run_schedule(const char *schedule, test_function tfunc)
             status(_("parallel group (%d tests): "), num_tests);
             for (i = 0; i < num_tests; i++)
             {
-                pids[i] = (tfunc) (tests[i], &resultfiles[i], &expectfiles[i], &tags[i]);
+                pids[i] = (startfunc) (tests[i], &resultfiles[i], &expectfiles[i], &tags[i]);
                 INSTR_TIME_SET_CURRENT(starttimes[i]);
             }
             wait_for_tests(pids, statuses, stoptimes, tests, num_tests);
@@ -1801,6 +1802,8 @@ run_schedule(const char *schedule, test_function tfunc)
             {
                 bool        newdiff;

+                if (postfunc)
+                    (*postfunc) (rl->str);
                 newdiff = results_differ(tests[i], rl->str, el->str);
                 if (newdiff && tl)
                 {
@@ -1867,7 +1870,8 @@ run_schedule(const char *schedule, test_function tfunc)
  * Run a single test
  */
 static void
-run_single_test(const char *test, test_function tfunc)
+run_single_test(const char *test, test_start_function startfunc,
+                postprocess_result_function postfunc)
 {
     PID_TYPE    pid;
     instr_time    starttime;
@@ -1882,7 +1886,7 @@ run_single_test(const char *test, test_function tfunc)
     bool        differ = false;

     status(_("test %-28s ... "), test);
-    pid = (tfunc) (test, &resultfiles, &expectfiles, &tags);
+    pid = (startfunc) (test, &resultfiles, &expectfiles, &tags);
     INSTR_TIME_SET_CURRENT(starttime);
     wait_for_tests(&pid, &exit_status, &stoptime, NULL, 1);

@@ -1900,6 +1904,8 @@ run_single_test(const char *test, test_function tfunc)
     {
         bool        newdiff;

+        if (postfunc)
+            (*postfunc) (rl->str);
         newdiff = results_differ(test, rl->str, el->str);
         if (newdiff && tl)
         {
@@ -2083,7 +2089,10 @@ help(void)
 }

 int
-regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc)
+regression_main(int argc, char *argv[],
+                init_function ifunc,
+                test_start_function startfunc,
+                postprocess_result_function postfunc)
 {
     static struct option long_options[] = {
         {"help", no_argument, NULL, 'h'},
@@ -2554,12 +2563,12 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc

     for (sl = schedulelist; sl != NULL; sl = sl->next)
     {
-        run_schedule(sl->str, tfunc);
+        run_schedule(sl->str, startfunc, postfunc);
     }

     for (sl = extra_tests; sl != NULL; sl = sl->next)
     {
-        run_single_test(sl->str, tfunc);
+        run_single_test(sl->str, startfunc, postfunc);
     }

     /*
diff --git a/src/test/regress/pg_regress.h b/src/test/regress/pg_regress.h
index d04f7721aa..c6d015c840 100644
--- a/src/test/regress/pg_regress.h
+++ b/src/test/regress/pg_regress.h
@@ -27,12 +27,23 @@ typedef struct _stringlist
     struct _stringlist *next;
 } _stringlist;

-typedef PID_TYPE(*test_function) (const char *,
-                                  _stringlist **,
-                                  _stringlist **,
-                                  _stringlist **);
+/*
+ * Callback function signatures for test programs that use regression_main()
+ */
+
+/* Initialize at program start */
 typedef void (*init_function) (int argc, char **argv);

+/* Launch one test case */
+typedef PID_TYPE(*test_start_function) (const char *testname,
+                                        _stringlist **resultfiles,
+                                        _stringlist **expectfiles,
+                                        _stringlist **tags);
+
+/* Postprocess one result file (optional) */
+typedef void (*postprocess_result_function) (const char *filename);
+
+
 extern char *bindir;
 extern char *libdir;
 extern char *datadir;
@@ -48,7 +59,10 @@ extern const char *basic_diff_opts;
 extern const char *pretty_diff_opts;

 int            regression_main(int argc, char *argv[],
-                            init_function ifunc, test_function tfunc);
+                            init_function ifunc,
+                            test_start_function startfunc,
+                            postprocess_result_function postfunc);
+
 void        add_stringlist_item(_stringlist **listhead, const char *str);
 PID_TYPE    spawn_process(const char *cmdline);
 void        replace_string(struct StringInfoData *string,
diff --git a/src/test/regress/pg_regress_main.c b/src/test/regress/pg_regress_main.c
index a218bae247..8dc4941c24 100644
--- a/src/test/regress/pg_regress_main.c
+++ b/src/test/regress/pg_regress_main.c
@@ -119,5 +119,8 @@ psql_init(int argc, char **argv)
 int
 main(int argc, char *argv[])
 {
-    return regression_main(argc, argv, psql_init, psql_start_test);
+    return regression_main(argc, argv,
+                           psql_init,
+                           psql_start_test,
+                           NULL /* no postfunc needed */ );
 }

Hi Tom,

I agree to get detailed error message for each failed host as your patch 0001.

As for patch 0004, find ':' after "could not connect to" may failed when error message like:
"could not connect to host "localhost" (::1), port 12345: Connection refused", where p2 will point to "::1" instead of ": Connection refused". But since it's only used for test case, we don't need to filter the error message precisely.

```
ecpg_filter_stderr(const char *resultfile, const char *tmpfile)
{
    ......
    char       *p1 = strstr(linebuf.data, "could not connect to ");
    if (p1)
    {
        char       *p2 = strchr(p1, ':');
        if (p2)
            memmove(p1 + 17, p2, strlen(p2) + 1);
    }
}
```

Thanks,
Hubert


From: Tom Lane <tgl@sss.pgh.pa.us>
Sent: Monday, January 11, 2021 10:56 AM
To: Hubert Zhang <zhubert@vmware.com>
Cc: tsunakawa.takay@fujitsu.com <tsunakawa.takay@fujitsu.com>; pgsql-hackers@postgresql.org <pgsql-hackers@postgresql.org>
Subject: Re: Multiple hosts in connection string failed to failover in non-hot standby mode
 
I wrote:
> I feel pretty good about 0001: it might be committable as-is.  0002 is
> probably subject to bikeshedding, plus it has a problem in the ECPG tests.
> Two of the error messages are now unstable because they expose
> chosen-at-random socket paths:
> ...
> I don't have any non-hacky ideas what to do about that.  The extra detail
> seems useful to end users, but we don't have any infrastructure that
> would allow filtering it out in the ECPG tests.

So far the only solution that comes to mind is to introduce some
infrastructure to do that filtering.  0001-0003 below are unchanged,
0004 patches up the ecpg test framework with a rather ad-hoc filtering
function.  I'd feel worse about this if there weren't already a very
ad-hoc filtering function there ;-)

This set passes check-world for me; we'll soon see what the cfbot
thinks.

                        regards, tom lane

Hubert Zhang <zhubert@vmware.com> writes:
> As for patch 0004, find ':' after "could not connect to" may failed when error message like:
> "could not connect to host "localhost" (::1), port 12345: Connection refused", where p2 will point to "::1" instead
of": Connection refused". But since it's only used for test case, we don't need to filter the error message precisely. 

Excellent point, and I think that could happen on a Windows installation.
We can make it look for ": " instead of just ':', and that'll reduce the
odds of trouble.

            regards, tom lane



Justin Pryzby <pryzby@telsasoft.com> writes:
> 52a10224 broke sqlsmith, of all things.

> It was using errmsg as a test of success, instead of checking if the connection
> result wasn't null:

>      conn = PQconnectdb(conninfo.c_str());
>      char *errmsg = PQerrorMessage(conn);
>      if (strlen(errmsg))
>          throw dut::broken(errmsg, "08001");

> That's clearly the wrong thing to do, but maybe this should be described in the
> release notes as a compatibility issue, in case other people had the same idea.
> Clearing errorMessage during success is an option..

Hm.  I'd debated whether to clear conn->errorMessage at the end of
a successful connection sequence, and decided not to on the grounds
that it might be interesting info (eg it could tell you why you
ended up connected to server Y and not server X).  But perhaps
it's too much of a compatibility break for this small benefit.

I'm curious though why it took this long for anyone to complain.
I'd supposed that people were running sqlsmith against HEAD on
a pretty regular basis.

            regards, tom lane



Re: Multiple hosts in connection string failed to failover in non-hot standby mode

From
Andreas Seltenreich
Date:
> On Thu, May 06, 2021 at 01:22:27PM -0400, Tom Lane wrote:
>> I'm curious though why it took this long for anyone to complain.
>> I'd supposed that people were running sqlsmith against HEAD on
>> a pretty regular basis.

Last time I ran it was November 27.  I'm neglecting it on my spare time
and there is hardly any opportunity to sneak it onto my agenda at work.
I'll do my best to try to get either of these fixed.

Justin Pryzby writes:
> I think it's also becase sqlsmith would need to run against the v14 *client*
> library.  I don't know about anyone else's workflow, but I tend not to "make
> install", but work with binaries out of ./tmp_install.

My playbooks don't grab the client libraries of the test target either.
I'll change them.

> There's a few changes needed on sqlsmith HEAD, but I guess nobody would have
> gotten that far if the connection was failing (or rather, detected as such).

Thanks for the patch.

regards,
andreas



On Thu, May 06, 2021 at 01:22:27PM -0400, Tom Lane wrote:
> Hm.  I'd debated whether to clear conn->errorMessage at the end of
> a successful connection sequence, and decided not to on the grounds
> that it might be interesting info (eg it could tell you why you
> ended up connected to server Y and not server X).  But perhaps
> it's too much of a compatibility break for this small benefit.
>
> I'm curious though why it took this long for anyone to complain.
> I'd supposed that people were running sqlsmith against HEAD on
> a pretty regular basis.

FWIW, I think that the case of getting some information about any
failed connections while a connection has been successfully made
within the scope of the connection string parameters provided by the
user is rather thin, and I really feel that this is going to cause
more pain to users than this is worth it.  So my vote would be to
clean up conn->errorMessage after a successful connection.

Now, I would not mind either if we finish by taking a decision here
after beta1, to see if there are actual complains on the matter based
on the feedback we get.
--
Michael

Attachment
> On 11 May 2021, at 09:29, Michael Paquier <michael@paquier.xyz> wrote:

> FWIW, I think that the case of getting some information about any
> failed connections while a connection has been successfully made
> within the scope of the connection string parameters provided by the
> user is rather thin, and I really feel that this is going to cause
> more pain to users than this is worth it.  So my vote would be to
> clean up conn->errorMessage after a successful connection.

Agreed, given how conservative we typically are with backwards compatibility it
seems a too thin benefit to warrant potential breakage.

My vote would too be to restore the behavior by clearing conn->errorMessage.

--
Daniel Gustafsson        https://vmware.com/


On Sun, May 30, 2021 at 08:25:00PM -0500, Justin Pryzby wrote:
> ..But I think it's not useful to put details into errorMessage on success,
> unless you're going to document that.  It would never have occurred to me to
> look there, or that it would even be safe.

Yeah.  On the contrary, it could be confusing if one sees an error
message but there is nothing to worry about, because things are
working in the scope of what the user wanted at connection time.
--
Michael

Attachment
On Mon, May 31, 2021 at 11:00:55AM +0900, Michael Paquier wrote:
> Yeah.  On the contrary, it could be confusing if one sees an error
> message but there is nothing to worry about, because things are
> working in the scope of what the user wanted at connection time.

In my recent quest to look at GSSAPI builds on Windows, I have bumped
into another failure that's related to this thread.  hamerkop
summarizes the situation here:
https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=hamerkop&dt=2021-05-29%2010%3A15%3A42

There are two failures like this one as errorMessage piles up on
failures, as of connect/test5:
-[NO_PID]: ECPGconnect: connection to server failed: FATAL:  database
 "regress_ecpg_user2" does not exist
+[NO_PID]: ECPGconnect: connection to server failed: could not
 initiate GSSAPI security context: Unspecified GSS failure.  Minor
 code may provide more information: Credential cache is empty
+connection to server failed: FATAL:  database "regress_ecpg_user2"
 does not exist
--
Michael

Attachment
Michael Paquier <michael@paquier.xyz> writes:
> In my recent quest to look at GSSAPI builds on Windows, I have bumped
> into another failure that's related to this thread.  hamerkop
> summarizes the situation here:
> https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=hamerkop&dt=2021-05-29%2010%3A15%3A42
> There are two failures like this one as errorMessage piles up on
> failures, as of connect/test5:
> -[NO_PID]: ECPGconnect: connection to server failed: FATAL:  database
>  "regress_ecpg_user2" does not exist
> +[NO_PID]: ECPGconnect: connection to server failed: could not
>  initiate GSSAPI security context: Unspecified GSS failure.  Minor
>  code may provide more information: Credential cache is empty
> +connection to server failed: FATAL:  database "regress_ecpg_user2"
>  does not exist

Yeah, I was looking at that earlier today.  Evidently libpq is
trying a GSS-encrypted connection, and that doesn't work, so
it falls back to a regular connection where we get the expected
error.  Probably all the connections in this test are hitting the
GSS failure, but only the ones where the second attempt fails
show a visible issue.

What is not clear is why GSS is acting that way.  We wouldn't
have tried a GSS connection unless pg_GSS_have_cred_cache
succeeded ... so how come that worked but then gss_init_sec_context
complained "Credential cache is empty"?

My rough guess is that Windows has implemented the GSS APIs in
such a way that what pg_GSS_have_cred_cache is testing isn't
sufficient to tell whether a sane credential actually exists.

Or there's something particularly weird about how hamerkop
is set up.

            regards, tom lane



On Mon, May 31, 2021 at 12:05:12AM -0400, Tom Lane wrote:
> Yeah, I was looking at that earlier today.  Evidently libpq is
> trying a GSS-encrypted connection, and that doesn't work, so
> it falls back to a regular connection where we get the expected
> error.  Probably all the connections in this test are hitting the
> GSS failure, but only the ones where the second attempt fails
> show a visible issue.

Yep.  This wastes cycles.

> What is not clear is why GSS is acting that way.  We wouldn't
> have tried a GSS connection unless pg_GSS_have_cred_cache
> succeeded ... so how come that worked but then gss_init_sec_context
> complained "Credential cache is empty"?
>
> My rough guess is that Windows has implemented the GSS APIs in
> such a way that what pg_GSS_have_cred_cache is testing isn't
> sufficient to tell whether a sane credential actually exists.
>
> Or there's something particularly weird about how hamerkop
> is set up.

I suspect that's just the way the upstream installation works with a
credentials cache created from the beginning, as I see the same
behavior and the same error on my own host for HEAD with a KRB5 server
set up once the upstream installation runs.  Leaving the specific
topic of this thread aside for one moment, would there be an argument
for just enforcing gssencmode=disable in this set of tests down to 12?
It is worth noting that the problem does not show up in 12 and 13 once
the compilation works, because we just mask the error there, but the
code path is still taken.

Another thing that strikes me as incorrect is that we don't unset
PGGSSENCMODE or PGGSSLIB in TestLib.pm.  Just noting it on the way..
--
Michael

Attachment
Michael Paquier <michael@paquier.xyz> writes:
> On Mon, May 31, 2021 at 12:05:12AM -0400, Tom Lane wrote:
>> What is not clear is why GSS is acting that way.  We wouldn't
>> have tried a GSS connection unless pg_GSS_have_cred_cache
>> succeeded ... so how come that worked but then gss_init_sec_context
>> complained "Credential cache is empty"?

> I suspect that's just the way the upstream installation works with a
> credentials cache created from the beginning, as I see the same
> behavior and the same error on my own host for HEAD with a KRB5 server
> set up once the upstream installation runs.

Interesting --- I was considering running such a test locally, but
didn't get round to it yet.

> Leaving the specific
> topic of this thread aside for one moment, would there be an argument
> for just enforcing gssencmode=disable in this set of tests down to 12?

It seems like the ideal solution would be to make pg_GSS_have_cred_cache
smarter, so that we don't attempt a GSS connection cycle here.  But if
we can't, adding gssencmode=disable to these test cases is what I was
thinking about, too.

> Another thing that strikes me as incorrect is that we don't unset
> PGGSSENCMODE or PGGSSLIB in TestLib.pm.  Just noting it on the way..

Agreed, that seems bogus.

            regards, tom lane



On Mon, May 31, 2021 at 09:36:20AM -0400, Tom Lane wrote:
> Interesting --- I was considering running such a test locally, but
> didn't get round to it yet.

Just to be clear, that's my Windows dev box.

> It seems like the ideal solution would be to make pg_GSS_have_cred_cache
> smarter, so that we don't attempt a GSS connection cycle here.

I am not sure yet what would be adapted here.  That requires diving a
bit into the upstream code.

>> Another thing that strikes me as incorrect is that we don't unset
>> PGGSSENCMODE or PGGSSLIB in TestLib.pm.  Just noting it on the way..
>
> Agreed, that seems bogus.

There may be others, and I have not checked yet.  I'd rather do a
backpatch for this part, would you agree?
--
Michael

Attachment
Michael Paquier <michael@paquier.xyz> writes:
>>> Another thing that strikes me as incorrect is that we don't unset
>>> PGGSSENCMODE or PGGSSLIB in TestLib.pm.  Just noting it on the way..

>> Agreed, that seems bogus.

> There may be others, and I have not checked yet.  I'd rather do a
> backpatch for this part, would you agree?

+1

            regards, tom lane



On Mon, May 31, 2021 at 09:07:38PM -0400, Tom Lane wrote:
> Michael Paquier <michael@paquier.xyz> writes:
>>> Agreed, that seems bogus.
>
>> There may be others, and I have not checked yet.  I'd rather do a
>> backpatch for this part, would you agree?
>
> +1

Playing with all those variables and broken values here and there, I
have been able to break a bunch of tests.  Most of the failures were
in the authentication and SSL tests, but there were also fancier
cases.  For example, PGCLIENTENCODING would cause a failure with
pg_ctl, for any TAP test.

I got surprised that enforcing values for most of the PGSSL* ones did
not cause a failure when it came to the certs, CRLs keys and root
certs now.  Still, I think that we'd be safer to cancel these as
well.

Attached is the list I am finishing with.  I'd like to fix that, so
please let me know if there are any comments or objections.
--
Michael

Attachment
It seems like nobody's terribly interested in figuring out why
pg_GSS_have_cred_cache() is misbehaving on Windows.  So I took
a look at disabling GSSENC in these test cases to try to silence
hamerkop's test failure that way.  Here's a proposed patch.
It relies on setenv() being available, but I think that's fine
because we link the ECPG test programs with libpgport.

            regards, tom lane

diff --git a/src/interfaces/ecpg/test/connect/test5.pgc b/src/interfaces/ecpg/test/connect/test5.pgc
index e712fa8778..f7263935ce 100644
--- a/src/interfaces/ecpg/test/connect/test5.pgc
+++ b/src/interfaces/ecpg/test/connect/test5.pgc
@@ -18,6 +18,9 @@ exec sql begin declare section;
     char *user="regress_ecpg_user1";
 exec sql end declare section;

+    /* disable GSSENC to ensure stability of connection failure reports */
+    setenv("PGGSSENCMODE", "disable", 1);
+
     ECPGdebug(1, stderr);

     exec sql connect to ecpg2_regression as main;
diff --git a/src/interfaces/ecpg/test/expected/connect-test5.c b/src/interfaces/ecpg/test/expected/connect-test5.c
index 6ae5b589de..a86a5e4331 100644
--- a/src/interfaces/ecpg/test/expected/connect-test5.c
+++ b/src/interfaces/ecpg/test/expected/connect-test5.c
@@ -38,37 +38,33 @@ main(void)
 #line 19 "test5.pgc"


+    /* disable GSSENC to ensure stability of connection failure reports */
+    setenv("PGGSSENCMODE", "disable", 1);
+
     ECPGdebug(1, stderr);

     { ECPGconnect(__LINE__, 0, "ecpg2_regression" , NULL, NULL , "main", 0); }
-#line 23 "test5.pgc"
+#line 26 "test5.pgc"

     { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "alter user regress_ecpg_user2 encrypted password 'insecure'",
ECPGt_EOIT,ECPGt_EORT);} 
-#line 24 "test5.pgc"
+#line 27 "test5.pgc"

     { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "alter user regress_ecpg_user1 encrypted password 'connectpw'",
ECPGt_EOIT,ECPGt_EORT);} 
-#line 25 "test5.pgc"
+#line 28 "test5.pgc"

     { ECPGtrans(__LINE__, NULL, "commit");}
-#line 26 "test5.pgc"
+#line 29 "test5.pgc"

     { ECPGdisconnect(__LINE__, "CURRENT");}
-#line 27 "test5.pgc"
+#line 30 "test5.pgc"
   /* <-- "main" not specified */

     strcpy(db, "ecpg2_regression");
     strcpy(id, "main");
     { ECPGconnect(__LINE__, 0, db , NULL, NULL , id, 0); }
-#line 31 "test5.pgc"
-
-    { ECPGdisconnect(__LINE__, id);}
-#line 32 "test5.pgc"
-
-
-    { ECPGconnect(__LINE__, 0, "ecpg2_regression" , NULL, NULL , "main", 0); }
 #line 34 "test5.pgc"

-    { ECPGdisconnect(__LINE__, "main");}
+    { ECPGdisconnect(__LINE__, id);}
 #line 35 "test5.pgc"


@@ -86,21 +82,21 @@ main(void)
 #line 41 "test5.pgc"


-    { ECPGconnect(__LINE__, 0, "" , "regress_ecpg_user2" , "insecure" , "main", 0); }
+    { ECPGconnect(__LINE__, 0, "ecpg2_regression" , NULL, NULL , "main", 0); }
 #line 43 "test5.pgc"

     { ECPGdisconnect(__LINE__, "main");}
 #line 44 "test5.pgc"


-    { ECPGconnect(__LINE__, 0, "ecpg2_regression" , "regress_ecpg_user1" , "connectpw" , "main", 0); }
+    { ECPGconnect(__LINE__, 0, "" , "regress_ecpg_user2" , "insecure" , "main", 0); }
 #line 46 "test5.pgc"

     { ECPGdisconnect(__LINE__, "main");}
 #line 47 "test5.pgc"


-    { ECPGconnect(__LINE__, 0, "unix:postgresql://localhost/ecpg2_regression" , "regress_ecpg_user1" , "connectpw" ,
"main",0); } 
+    { ECPGconnect(__LINE__, 0, "ecpg2_regression" , "regress_ecpg_user1" , "connectpw" , "main", 0); }
 #line 49 "test5.pgc"

     { ECPGdisconnect(__LINE__, "main");}
@@ -114,48 +110,55 @@ main(void)
 #line 53 "test5.pgc"


-    { ECPGconnect(__LINE__, 0, "unix:postgresql://localhost/ecpg2_regression" , user , "connectpw" , "main", 0); }
+    { ECPGconnect(__LINE__, 0, "unix:postgresql://localhost/ecpg2_regression" , "regress_ecpg_user1" , "connectpw" ,
"main",0); } 
 #line 55 "test5.pgc"

     { ECPGdisconnect(__LINE__, "main");}
 #line 56 "test5.pgc"


-    { ECPGconnect(__LINE__, 0, "unix:postgresql://localhost/ecpg2_regression?connect_timeout=180 &
client_encoding=latin1", "regress_ecpg_user1" , "connectpw" , "main", 0); } 
+    { ECPGconnect(__LINE__, 0, "unix:postgresql://localhost/ecpg2_regression" , user , "connectpw" , "main", 0); }
 #line 58 "test5.pgc"

     { ECPGdisconnect(__LINE__, "main");}
 #line 59 "test5.pgc"


-    { ECPGconnect(__LINE__, 0, "unix:postgresql://200.46.204.71/ecpg2_regression" , "regress_ecpg_user1" , "connectpw"
,"main", 0); } 
+    { ECPGconnect(__LINE__, 0, "unix:postgresql://localhost/ecpg2_regression?connect_timeout=180 &
client_encoding=latin1", "regress_ecpg_user1" , "connectpw" , "main", 0); } 
 #line 61 "test5.pgc"

     { ECPGdisconnect(__LINE__, "main");}
 #line 62 "test5.pgc"


-    { ECPGconnect(__LINE__, 0, "unix:postgresql://localhost/" , "regress_ecpg_user2" , "insecure" , "main", 0); }
+    { ECPGconnect(__LINE__, 0, "unix:postgresql://200.46.204.71/ecpg2_regression" , "regress_ecpg_user1" , "connectpw"
,"main", 0); } 
 #line 64 "test5.pgc"

     { ECPGdisconnect(__LINE__, "main");}
 #line 65 "test5.pgc"


+    { ECPGconnect(__LINE__, 0, "unix:postgresql://localhost/" , "regress_ecpg_user2" , "insecure" , "main", 0); }
+#line 67 "test5.pgc"
+
+    { ECPGdisconnect(__LINE__, "main");}
+#line 68 "test5.pgc"
+
+
     /* connect twice */
     { ECPGconnect(__LINE__, 0, "ecpg2_regression" , NULL, NULL , "main", 0); }
-#line 68 "test5.pgc"
+#line 71 "test5.pgc"

     { ECPGconnect(__LINE__, 0, "ecpg2_regression" , NULL, NULL , "main", 0); }
-#line 69 "test5.pgc"
+#line 72 "test5.pgc"

     { ECPGdisconnect(__LINE__, "main");}
-#line 70 "test5.pgc"
+#line 73 "test5.pgc"


     /* not connected */
     { ECPGdisconnect(__LINE__, "nonexistent");}
-#line 73 "test5.pgc"
+#line 76 "test5.pgc"


     return 0;
diff --git a/src/interfaces/ecpg/test/expected/connect-test5.stderr
b/src/interfaces/ecpg/test/expected/connect-test5.stderr
index a15f344320..abab1ba5a2 100644
--- a/src/interfaces/ecpg/test/expected/connect-test5.stderr
+++ b/src/interfaces/ecpg/test/expected/connect-test5.stderr
@@ -2,19 +2,19 @@
 [NO_PID]: sqlca: code: 0, state: 00000
 [NO_PID]: ECPGconnect: opening database ecpg2_regression on <DEFAULT> port <DEFAULT>
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 24: query: alter user regress_ecpg_user2 encrypted password 'insecure'; with 0
parameter(s)on connection main 
+[NO_PID]: ecpg_execute on line 27: query: alter user regress_ecpg_user2 encrypted password 'insecure'; with 0
parameter(s)on connection main 
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 24: using PQexec
+[NO_PID]: ecpg_execute on line 27: using PQexec
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_process_output on line 24: OK: ALTER ROLE
+[NO_PID]: ecpg_process_output on line 27: OK: ALTER ROLE
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 25: query: alter user regress_ecpg_user1 encrypted password 'connectpw'; with 0
parameter(s)on connection main 
+[NO_PID]: ecpg_execute on line 28: query: alter user regress_ecpg_user1 encrypted password 'connectpw'; with 0
parameter(s)on connection main 
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 25: using PQexec
+[NO_PID]: ecpg_execute on line 28: using PQexec
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_process_output on line 25: OK: ALTER ROLE
+[NO_PID]: ecpg_process_output on line 28: OK: ALTER ROLE
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ECPGtrans on line 26: action "commit"; connection "main"
+[NO_PID]: ECPGtrans on line 29: action "commit"; connection "main"
 [NO_PID]: sqlca: code: 0, state: 00000
 [NO_PID]: ecpg_finish: connection main closed
 [NO_PID]: sqlca: code: 0, state: 00000
@@ -40,9 +40,9 @@
 [NO_PID]: sqlca: code: 0, state: 00000
 [NO_PID]: ecpg_finish: connection main closed
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: raising sqlcode -402 on line 43: could not connect to database "<DEFAULT>" on line 43
+[NO_PID]: raising sqlcode -402 on line 46: could not connect to database "<DEFAULT>" on line 46
 [NO_PID]: sqlca: code: -402, state: 08001
-[NO_PID]: raising sqlcode -220 on line 44: connection "main" does not exist on line 44
+[NO_PID]: raising sqlcode -220 on line 47: connection "main" does not exist on line 47
 [NO_PID]: sqlca: code: -220, state: 08003
 [NO_PID]: ECPGconnect: opening database ecpg2_regression on <DEFAULT> port <DEFAULT>  for user regress_ecpg_user1
 [NO_PID]: sqlca: code: 0, state: 00000
@@ -64,11 +64,11 @@
 [NO_PID]: sqlca: code: 0, state: 00000
 [NO_PID]: ecpg_finish: connection main closed
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ECPGconnect: non-localhost access via sockets on line 61
+[NO_PID]: ECPGconnect: non-localhost access via sockets on line 64
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: raising sqlcode -402 on line 61: could not connect to database "ecpg2_regression" on line 61
+[NO_PID]: raising sqlcode -402 on line 64: could not connect to database "ecpg2_regression" on line 64
 [NO_PID]: sqlca: code: -402, state: 08001
-[NO_PID]: raising sqlcode -220 on line 62: connection "main" does not exist on line 62
+[NO_PID]: raising sqlcode -220 on line 65: connection "main" does not exist on line 65
 [NO_PID]: sqlca: code: -220, state: 08003
 [NO_PID]: ECPGconnect: opening database <DEFAULT> on <DEFAULT> port <DEFAULT>  for user regress_ecpg_user2
 [NO_PID]: sqlca: code: 0, state: 00000
@@ -76,9 +76,9 @@
 [NO_PID]: sqlca: code: 0, state: 00000
 [NO_PID]: ecpg_finish: connection main closed
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: raising sqlcode -402 on line 64: could not connect to database "<DEFAULT>" on line 64
+[NO_PID]: raising sqlcode -402 on line 67: could not connect to database "<DEFAULT>" on line 67
 [NO_PID]: sqlca: code: -402, state: 08001
-[NO_PID]: raising sqlcode -220 on line 65: connection "main" does not exist on line 65
+[NO_PID]: raising sqlcode -220 on line 68: connection "main" does not exist on line 68
 [NO_PID]: sqlca: code: -220, state: 08003
 [NO_PID]: ECPGconnect: opening database ecpg2_regression on <DEFAULT> port <DEFAULT>
 [NO_PID]: sqlca: code: 0, state: 00000
@@ -86,5 +86,5 @@
 [NO_PID]: sqlca: code: 0, state: 00000
 [NO_PID]: ecpg_finish: connection main closed
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: raising sqlcode -220 on line 73: connection "nonexistent" does not exist on line 73
+[NO_PID]: raising sqlcode -220 on line 76: connection "nonexistent" does not exist on line 76
 [NO_PID]: sqlca: code: -220, state: 08003

On Sun, Jun 06, 2021 at 05:27:49PM -0400, Tom Lane wrote:
> It seems like nobody's terribly interested in figuring out why
> pg_GSS_have_cred_cache() is misbehaving on Windows.

I have been investigating that for a couple of hours in total, but
nothing to report yet.

> So I took
> a look at disabling GSSENC in these test cases to try to silence
> hamerkop's test failure that way.  Here's a proposed patch.
> It relies on setenv() being available, but I think that's fine
> because we link the ECPG test programs with libpgport.

No, that's not it.  The compilation of the tests happens when
triggering the tests as of ecpgcheck() in vcregress.pl so I think that
this is going to fail.  This requires at least the addition of a
reference to libpgport in ecpg_regression.proj, perhaps more.
--
Michael

Attachment
Michael Paquier <michael@paquier.xyz> writes:
> On Sun, Jun 06, 2021 at 05:27:49PM -0400, Tom Lane wrote:
>> So I took
>> a look at disabling GSSENC in these test cases to try to silence
>> hamerkop's test failure that way.  Here's a proposed patch.
>> It relies on setenv() being available, but I think that's fine
>> because we link the ECPG test programs with libpgport.

> No, that's not it.  The compilation of the tests happens when
> triggering the tests as of ecpgcheck() in vcregress.pl so I think that
> this is going to fail.  This requires at least the addition of a
> reference to libpgport in ecpg_regression.proj, perhaps more.

Hmm.  We do include "-lpgcommon -lpgport" when building the ecpg test
programs on Unix, so I'd assumed that the MSVC scripts did the same.
Is there a good reason not to make them do so?

            regards, tom lane



On Mon, Jun 07, 2021 at 10:38:03AM -0400, Tom Lane wrote:
> Hmm.  We do include "-lpgcommon -lpgport" when building the ecpg test
> programs on Unix, so I'd assumed that the MSVC scripts did the same.
> Is there a good reason not to make them do so?

I was looking at that this morning, and yes we need to add more
references here.  Actually, adding only libpgport.lib allows the
compilation and the tests to work, but I agree to add also
libpgcommon.lib so as we don't fall into the same compilation trap
again in the future.

Now, I also see that using pgwin32_setenv() instead of
src/port/setenv.c causes cl to be confused once we update
ecpg_regression.proj because it cannot find setenv().  Bringing the
question, why is it necessary to have both setenv.c and
pgwin32_setenv() on HEAD?  setenv.c should be enough once you have the
fallback implementation of putenv() available.

Attached is the patch I am finishing with, that also brings all this
stuff closer to what I did in 12 and 13 for hamerkop.  The failing
test is passing for me now with MSVC and GSSAPI builds.

Thoughts?
--
Michael

Attachment
Michael Paquier <michael@paquier.xyz> writes:
> Now, I also see that using pgwin32_setenv() instead of
> src/port/setenv.c causes cl to be confused once we update
> ecpg_regression.proj because it cannot find setenv().  Bringing the
> question, why is it necessary to have both setenv.c and
> pgwin32_setenv() on HEAD?  setenv.c should be enough once you have the
> fallback implementation of putenv() available.

IIUC, what you are proposing to do is replace pgwin32_setenv with
src/port/setenv.c.  I don't think that's an improvement.  setenv.c
leaks memory on repeat calls, because it cannot know what
pgwin32_setenv knows about how putenv works on that platform.

It'd be okay to do it like that for the ECPG tests, perhaps,
because we don't really care about small leaks in those.
But I don't want it to happen across-the-board.

Thinking more, the real problem is that use of libpgport
goes hand-in-hand with #including port.h; it's not going
to work real well if you do one without the other.
And I don't think we want to include port.h in the ECPG
test programs, because those are trying to model the
environment that typical user applications see.

Alternatives seem to be

(1) allow just this one ECPG test to include port.h (or
probably c.h).  However, there's a whole other can of worms
there, which is that I wonder if we aren't doing it wrong
on the Unix side by linking libpgport when we shouldn't.
We've not been bit by that yet, but I wonder if it isn't
just a matter of time.  The MSVC build, by not linking
those libs in the first place, is really doing this the
correct way.

(2) Let pg_regress_ecpg.c pass down the environment setting.

(3) Don't try to use the environment variable for this
purpose.  I'd originally tried to change test5.pgc to just
specify gssmode=disable in-line, but that only works
nicely for one of the two failing cases.  The other one
is testing the case of a completely defaulted connection
target, so there's no place to add an option without
breaking the only unique aspect of that test case.

(2) is starting to seem attractive now that we've seen
the downsides of (1) and (3).

(BTW, I just noticed that regress.c is unsetenv'ing the
SSL connection environment variables, but not the GSS ones.
Seens like that needs work similar to 8279f68a1.)

            regards, tom lane



On Tue, Jun 08, 2021 at 11:21:34AM -0400, Tom Lane wrote:
> IIUC, what you are proposing to do is replace pgwin32_setenv with
> src/port/setenv.c.  I don't think that's an improvement.  setenv.c
> leaks memory on repeat calls, because it cannot know what
> pgwin32_setenv knows about how putenv works on that platform.

Is gaur the only animal that needs this file, by the way?

> (1) allow just this one ECPG test to include port.h (or
> probably c.h).  However, there's a whole other can of worms
> there, which is that I wonder if we aren't doing it wrong
> on the Unix side by linking libpgport when we shouldn't.
> We've not been bit by that yet, but I wonder if it isn't
> just a matter of time.  The MSVC build, by not linking
> those libs in the first place, is really doing this the
> correct way.

I don't really want to include this stuff in the ECPG tests just to
bypass an environment configuration.

> (2) Let pg_regress_ecpg.c pass down the environment setting.
>
> (3) Don't try to use the environment variable for this
> purpose.  I'd originally tried to change test5.pgc to just
> specify gssmode=disable in-line, but that only works
> nicely for one of the two failing cases.  The other one
> is testing the case of a completely defaulted connection
> target, so there's no place to add an option without
> breaking the only unique aspect of that test case.

> (2) is starting to seem attractive now that we've seen
> the downsides of (1) and (3).

FWIW, I'd be rather in favor of doing (3) because this remains simple
just to take care of an edge case, even if that partially breaks the
promise to rely on a default connection.

(4) would be to revisit the decision to make libpq report all the
errors stored in its stack with multiple attempts.  That would bring
back the buildfarm to green at least, and we still need to take a
decision about that for 14 anyway as it involves a compatibility
breakage.  But I agree that we also should do something for 12~ for
those tests.

> (BTW, I just noticed that regress.c is unsetenv'ing the
> SSL connection environment variables, but not the GSS ones.
> Seens like that needs work similar to 8279f68a1.)

Yes, I saw that I was able to break things is many fancy ways when
working on 8279f68a1, but the list of parameters to reset needs to
diverge a bit compared to the TAP tests.
--
Michael

Attachment
Michael Paquier <michael@paquier.xyz> writes:
> On Tue, Jun 08, 2021 at 11:21:34AM -0400, Tom Lane wrote:
>> IIUC, what you are proposing to do is replace pgwin32_setenv with
>> src/port/setenv.c.  I don't think that's an improvement.  setenv.c
>> leaks memory on repeat calls, because it cannot know what
>> pgwin32_setenv knows about how putenv works on that platform.

> Is gaur the only animal that needs this file, by the way?

I think it is.  setenv has been in POSIX for awhile, so probably
only very old systems would need that.  (This is why I don't care
that much that setenv.c leaks memory.  But we can't start using it
on platforms where we *do* care about performance.)

>> (3) Don't try to use the environment variable for this
>> purpose.  I'd originally tried to change test5.pgc to just
>> specify gssmode=disable in-line, but that only works
>> nicely for one of the two failing cases.  The other one
>> is testing the case of a completely defaulted connection
>> target, so there's no place to add an option without
>> breaking the only unique aspect of that test case.

> FWIW, I'd be rather in favor of doing (3) because this remains simple
> just to take care of an edge case, even if that partially breaks the
> promise to rely on a default connection.

Yeah, it doesn't seem like we need to test that case all that
badly.  I'd be okay with dropping that test; or maybe we could
fix things so that the default case succeeds?

            regards, tom lane



I wrote:
> ...  I'd be okay with dropping that test; or maybe we could
> fix things so that the default case succeeds?

Here's a draft patch that renames regress_ecpg_user2 to ecpg2_regression,
which matches the name of one of the databases used, allowing the test
cases with defaulted database name to succeed.  That gets rid of one of
the problematic diffs.  As it stood, though, that meant that connect/test5
wasn't exercising the connection-failure code path at all, which didn't
seem like what we want.  So I adjusted the second place that had been
failing to again fail on no-such-database, and stuck in gssencmode=disable
so that we shouldn't get any test diff on hamerkop.

            regards, tom lane

diff --git a/src/interfaces/ecpg/test/Makefile b/src/interfaces/ecpg/test/Makefile
index be53b7b94d..abea3fcc85 100644
--- a/src/interfaces/ecpg/test/Makefile
+++ b/src/interfaces/ecpg/test/Makefile
@@ -77,7 +77,7 @@ $(remaining_files_build): $(abs_builddir)/%: $(srcdir)/%
 endif

 # Common options for tests. Also pick up anything passed in EXTRA_REGRESS_OPTS
-REGRESS_OPTS = --dbname=ecpg1_regression,ecpg2_regression --create-role=regress_ecpg_user1,regress_ecpg_user2
$(EXTRA_REGRESS_OPTS)
+REGRESS_OPTS = --dbname=ecpg1_regression,ecpg2_regression --create-role=regress_ecpg_user1,ecpg2_regression
$(EXTRA_REGRESS_OPTS)

 check: all
     $(with_temp_install) ./pg_regress $(REGRESS_OPTS) --temp-instance=./tmp_check $(TEMP_CONF) --bindir=
$(pg_regress_locale_flags)$(THREAD) --schedule=$(srcdir)/ecpg_schedule sql/twophase 
diff --git a/src/interfaces/ecpg/test/connect/test1.pgc b/src/interfaces/ecpg/test/connect/test1.pgc
index 961bd72ef2..77e571ec86 100644
--- a/src/interfaces/ecpg/test/connect/test1.pgc
+++ b/src/interfaces/ecpg/test/connect/test1.pgc
@@ -26,16 +26,16 @@ exec sql end declare section;
     exec sql connect to ecpg2_regression@localhost as main;
     exec sql disconnect main;

-    exec sql connect to @localhost as main user regress_ecpg_user2;
+    exec sql connect to @localhost as main user ecpg2_regression;
     exec sql disconnect main;

-    /* exec sql connect to :@TEMP_PORT@ as main user regress_ecpg_user2;
+    /* exec sql connect to :@TEMP_PORT@ as main user ecpg2_regression;
     exec sql disconnect main; */

     exec sql connect to tcp:postgresql://localhost/ecpg2_regression user regress_ecpg_user1 identified by connectpw;
     exec sql disconnect;

-    exec sql connect to tcp:postgresql://localhost/ user regress_ecpg_user2;
+    exec sql connect to tcp:postgresql://localhost/ user ecpg2_regression;
     exec sql disconnect;

     strcpy(pw, "connectpw");
diff --git a/src/interfaces/ecpg/test/connect/test5.pgc b/src/interfaces/ecpg/test/connect/test5.pgc
index e712fa8778..96a7b3e892 100644
--- a/src/interfaces/ecpg/test/connect/test5.pgc
+++ b/src/interfaces/ecpg/test/connect/test5.pgc
@@ -21,7 +21,7 @@ exec sql end declare section;
     ECPGdebug(1, stderr);

     exec sql connect to ecpg2_regression as main;
-    exec sql alter user regress_ecpg_user2 ENCRYPTED PASSWORD 'insecure';
+    exec sql alter user ecpg2_regression ENCRYPTED PASSWORD 'insecure';
     exec sql alter user regress_ecpg_user1 ENCRYPTED PASSWORD 'connectpw';
     exec sql commit;
     exec sql disconnect;  /* <-- "main" not specified */
@@ -40,7 +40,7 @@ exec sql end declare section;
     exec sql connect to 'ecpg2_regression' as main;
     exec sql disconnect main;

-    exec sql connect to as main user regress_ecpg_user2/insecure;
+    exec sql connect to as main user ecpg2_regression/insecure;
     exec sql disconnect main;

     exec sql connect to ecpg2_regression as main user regress_ecpg_user1/connectpw;
@@ -61,7 +61,7 @@ exec sql end declare section;
     exec sql connect to "unix:postgresql://200.46.204.71/ecpg2_regression" as main user regress_ecpg_user1/connectpw;
     exec sql disconnect main;

-    exec sql connect to unix:postgresql://localhost/ as main user regress_ecpg_user2 IDENTIFIED BY insecure;
+    exec sql connect to "unix:postgresql://localhost/no_such_db?gssencmode=disable" as main user ecpg2_regression
IDENTIFIEDBY insecure; 
     exec sql disconnect main;

     /* connect twice */
diff --git a/src/interfaces/ecpg/test/expected/connect-test1-minGW32.stderr
b/src/interfaces/ecpg/test/expected/connect-test1-minGW32.stderr
index c5b5248eb2..e5f16bd854 100644
--- a/src/interfaces/ecpg/test/expected/connect-test1-minGW32.stderr
+++ b/src/interfaces/ecpg/test/expected/connect-test1-minGW32.stderr
@@ -14,30 +14,18 @@
 [NO_PID]: sqlca: code: 0, state: 00000
 [NO_PID]: ecpg_finish: connection main closed
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ECPGconnect: opening database <DEFAULT> on localhost port <DEFAULT>  for user regress_ecpg_user2
-[NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ECPGconnect: connection to server failed: FATAL:  database "regress_ecpg_user2" does not exist
+[NO_PID]: ECPGconnect: opening database <DEFAULT> on localhost port <DEFAULT>  for user ecpg2_regression
 [NO_PID]: sqlca: code: 0, state: 00000
 [NO_PID]: ecpg_finish: connection main closed
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: raising sqlcode -402 on line 29: could not connect to database "<DEFAULT>" on line 29
-[NO_PID]: sqlca: code: -402, state: 08001
-[NO_PID]: raising sqlcode -220 on line 30: connection "main" does not exist on line 30
-[NO_PID]: sqlca: code: -220, state: 08003
 [NO_PID]: ECPGconnect: opening database ecpg2_regression on localhost port <DEFAULT>  for user regress_ecpg_user1
 [NO_PID]: sqlca: code: 0, state: 00000
 [NO_PID]: ecpg_finish: connection ecpg2_regression closed
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ECPGconnect: opening database <DEFAULT> on localhost port <DEFAULT>  for user regress_ecpg_user2
-[NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ECPGconnect: connection to server failed: FATAL:  database "regress_ecpg_user2" does not exist
+[NO_PID]: ECPGconnect: opening database <DEFAULT> on localhost port <DEFAULT>  for user ecpg2_regression
 [NO_PID]: sqlca: code: 0, state: 00000
 [NO_PID]: ecpg_finish: connection (null) closed
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: raising sqlcode -402 on line 38: could not connect to database "<DEFAULT>" on line 38
-[NO_PID]: sqlca: code: -402, state: 08001
-[NO_PID]: raising sqlcode -220 on line 39: connection "CURRENT" does not exist on line 39
-[NO_PID]: sqlca: code: -220, state: 08003
 [NO_PID]: ECPGconnect: opening database ecpg2_regression on localhost port <DEFAULT>  for user regress_ecpg_user1
 [NO_PID]: sqlca: code: 0, state: 00000
 [NO_PID]: ecpg_finish: connection ecpg2_regression closed
diff --git a/src/interfaces/ecpg/test/expected/connect-test1.c b/src/interfaces/ecpg/test/expected/connect-test1.c
index ffd24e2fc8..40d5aca4cd 100644
--- a/src/interfaces/ecpg/test/expected/connect-test1.c
+++ b/src/interfaces/ecpg/test/expected/connect-test1.c
@@ -53,14 +53,14 @@ main(void)
 #line 27 "test1.pgc"


-    { ECPGconnect(__LINE__, 0, "@localhost" , "regress_ecpg_user2" , NULL , "main", 0); }
+    { ECPGconnect(__LINE__, 0, "@localhost" , "ecpg2_regression" , NULL , "main", 0); }
 #line 29 "test1.pgc"

     { ECPGdisconnect(__LINE__, "main");}
 #line 30 "test1.pgc"


-    /* exec sql connect to :@TEMP_PORT@ as main user regress_ecpg_user2;
+    /* exec sql connect to :@TEMP_PORT@ as main user ecpg2_regression;
     exec sql disconnect main; */

     { ECPGconnect(__LINE__, 0, "tcp:postgresql://localhost/ecpg2_regression" , "regress_ecpg_user1" , "connectpw" ,
NULL,0); } 
@@ -70,7 +70,7 @@ main(void)
 #line 36 "test1.pgc"


-    { ECPGconnect(__LINE__, 0, "tcp:postgresql://localhost/" , "regress_ecpg_user2" , NULL , NULL, 0); }
+    { ECPGconnect(__LINE__, 0, "tcp:postgresql://localhost/" , "ecpg2_regression" , NULL , NULL, 0); }
 #line 38 "test1.pgc"

     { ECPGdisconnect(__LINE__, "CURRENT");}
diff --git a/src/interfaces/ecpg/test/expected/connect-test1.stderr
b/src/interfaces/ecpg/test/expected/connect-test1.stderr
index 073951c0ed..14f6a4ed5c 100644
--- a/src/interfaces/ecpg/test/expected/connect-test1.stderr
+++ b/src/interfaces/ecpg/test/expected/connect-test1.stderr
@@ -14,30 +14,18 @@
 [NO_PID]: sqlca: code: 0, state: 00000
 [NO_PID]: ecpg_finish: connection main closed
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ECPGconnect: opening database <DEFAULT> on localhost port <DEFAULT>  for user regress_ecpg_user2
-[NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ECPGconnect: connection to server failed: FATAL:  database "regress_ecpg_user2" does not exist
+[NO_PID]: ECPGconnect: opening database <DEFAULT> on localhost port <DEFAULT>  for user ecpg2_regression
 [NO_PID]: sqlca: code: 0, state: 00000
 [NO_PID]: ecpg_finish: connection main closed
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: raising sqlcode -402 on line 29: could not connect to database "<DEFAULT>" on line 29
-[NO_PID]: sqlca: code: -402, state: 08001
-[NO_PID]: raising sqlcode -220 on line 30: connection "main" does not exist on line 30
-[NO_PID]: sqlca: code: -220, state: 08003
 [NO_PID]: ECPGconnect: opening database ecpg2_regression on localhost port <DEFAULT>  for user regress_ecpg_user1
 [NO_PID]: sqlca: code: 0, state: 00000
 [NO_PID]: ecpg_finish: connection ecpg2_regression closed
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ECPGconnect: opening database <DEFAULT> on localhost port <DEFAULT>  for user regress_ecpg_user2
-[NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ECPGconnect: connection to server failed: FATAL:  database "regress_ecpg_user2" does not exist
+[NO_PID]: ECPGconnect: opening database <DEFAULT> on localhost port <DEFAULT>  for user ecpg2_regression
 [NO_PID]: sqlca: code: 0, state: 00000
 [NO_PID]: ecpg_finish: connection (null) closed
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: raising sqlcode -402 on line 38: could not connect to database "<DEFAULT>" on line 38
-[NO_PID]: sqlca: code: -402, state: 08001
-[NO_PID]: raising sqlcode -220 on line 39: connection "CURRENT" does not exist on line 39
-[NO_PID]: sqlca: code: -220, state: 08003
 [NO_PID]: ECPGconnect: opening database ecpg2_regression on localhost port <DEFAULT>  for user regress_ecpg_user1
 [NO_PID]: sqlca: code: 0, state: 00000
 [NO_PID]: ecpg_finish: connection ecpg2_regression closed
diff --git a/src/interfaces/ecpg/test/expected/connect-test5.c b/src/interfaces/ecpg/test/expected/connect-test5.c
index 6ae5b589de..c3543f4b57 100644
--- a/src/interfaces/ecpg/test/expected/connect-test5.c
+++ b/src/interfaces/ecpg/test/expected/connect-test5.c
@@ -43,7 +43,7 @@ main(void)
     { ECPGconnect(__LINE__, 0, "ecpg2_regression" , NULL, NULL , "main", 0); }
 #line 23 "test5.pgc"

-    { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "alter user regress_ecpg_user2 encrypted password 'insecure'",
ECPGt_EOIT,ECPGt_EORT);} 
+    { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "alter user ecpg2_regression encrypted password 'insecure'",
ECPGt_EOIT,ECPGt_EORT);} 
 #line 24 "test5.pgc"

     { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "alter user regress_ecpg_user1 encrypted password 'connectpw'",
ECPGt_EOIT,ECPGt_EORT);} 
@@ -86,7 +86,7 @@ main(void)
 #line 41 "test5.pgc"


-    { ECPGconnect(__LINE__, 0, "" , "regress_ecpg_user2" , "insecure" , "main", 0); }
+    { ECPGconnect(__LINE__, 0, "" , "ecpg2_regression" , "insecure" , "main", 0); }
 #line 43 "test5.pgc"

     { ECPGdisconnect(__LINE__, "main");}
@@ -135,7 +135,7 @@ main(void)
 #line 62 "test5.pgc"


-    { ECPGconnect(__LINE__, 0, "unix:postgresql://localhost/" , "regress_ecpg_user2" , "insecure" , "main", 0); }
+    { ECPGconnect(__LINE__, 0, "unix:postgresql://localhost/no_such_db?gssencmode=disable" , "ecpg2_regression" ,
"insecure", "main", 0); } 
 #line 64 "test5.pgc"

     { ECPGdisconnect(__LINE__, "main");}
diff --git a/src/interfaces/ecpg/test/expected/connect-test5.stderr
b/src/interfaces/ecpg/test/expected/connect-test5.stderr
index a15f344320..c5fe86f4c5 100644
--- a/src/interfaces/ecpg/test/expected/connect-test5.stderr
+++ b/src/interfaces/ecpg/test/expected/connect-test5.stderr
@@ -2,7 +2,7 @@
 [NO_PID]: sqlca: code: 0, state: 00000
 [NO_PID]: ECPGconnect: opening database ecpg2_regression on <DEFAULT> port <DEFAULT>
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 24: query: alter user regress_ecpg_user2 encrypted password 'insecure'; with 0
parameter(s)on connection main 
+[NO_PID]: ecpg_execute on line 24: query: alter user ecpg2_regression encrypted password 'insecure'; with 0
parameter(s)on connection main 
 [NO_PID]: sqlca: code: 0, state: 00000
 [NO_PID]: ecpg_execute on line 24: using PQexec
 [NO_PID]: sqlca: code: 0, state: 00000
@@ -34,16 +34,10 @@
 [NO_PID]: sqlca: code: 0, state: 00000
 [NO_PID]: ecpg_finish: connection main closed
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ECPGconnect: opening database <DEFAULT> on <DEFAULT> port <DEFAULT>  for user regress_ecpg_user2
-[NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ECPGconnect: connection to server failed: FATAL:  database "regress_ecpg_user2" does not exist
+[NO_PID]: ECPGconnect: opening database <DEFAULT> on <DEFAULT> port <DEFAULT>  for user ecpg2_regression
 [NO_PID]: sqlca: code: 0, state: 00000
 [NO_PID]: ecpg_finish: connection main closed
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: raising sqlcode -402 on line 43: could not connect to database "<DEFAULT>" on line 43
-[NO_PID]: sqlca: code: -402, state: 08001
-[NO_PID]: raising sqlcode -220 on line 44: connection "main" does not exist on line 44
-[NO_PID]: sqlca: code: -220, state: 08003
 [NO_PID]: ECPGconnect: opening database ecpg2_regression on <DEFAULT> port <DEFAULT>  for user regress_ecpg_user1
 [NO_PID]: sqlca: code: 0, state: 00000
 [NO_PID]: ecpg_finish: connection main closed
@@ -70,13 +64,13 @@
 [NO_PID]: sqlca: code: -402, state: 08001
 [NO_PID]: raising sqlcode -220 on line 62: connection "main" does not exist on line 62
 [NO_PID]: sqlca: code: -220, state: 08003
-[NO_PID]: ECPGconnect: opening database <DEFAULT> on <DEFAULT> port <DEFAULT>  for user regress_ecpg_user2
+[NO_PID]: ECPGconnect: opening database no_such_db on <DEFAULT> port <DEFAULT> with options gssencmode=disable for
userecpg2_regression 
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ECPGconnect: connection to server failed: FATAL:  database "regress_ecpg_user2" does not exist
+[NO_PID]: ECPGconnect: connection to server failed: FATAL:  database "no_such_db" does not exist
 [NO_PID]: sqlca: code: 0, state: 00000
 [NO_PID]: ecpg_finish: connection main closed
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: raising sqlcode -402 on line 64: could not connect to database "<DEFAULT>" on line 64
+[NO_PID]: raising sqlcode -402 on line 64: could not connect to database "no_such_db" on line 64
 [NO_PID]: sqlca: code: -402, state: 08001
 [NO_PID]: raising sqlcode -220 on line 65: connection "main" does not exist on line 65
 [NO_PID]: sqlca: code: -220, state: 08003
diff --git a/src/interfaces/ecpg/test/regression.h b/src/interfaces/ecpg/test/regression.h
index 6b7fba1bfd..68ab1417a2 100644
--- a/src/interfaces/ecpg/test/regression.h
+++ b/src/interfaces/ecpg/test/regression.h
@@ -2,4 +2,4 @@ exec        sql define REGRESSDB1 ecpg1_regression;
 exec        sql define REGRESSDB2 ecpg2_regression;

 exec        sql define REGRESSUSER1 regress_ecpg_user1;
-exec        sql define REGRESSUSER2 regress_ecpg_user2;
+exec        sql define REGRESSUSER2 ecpg2_regression;
diff --git a/src/tools/msvc/vcregress.pl b/src/tools/msvc/vcregress.pl
index 35e8f67f01..e7a15bbaa6 100644
--- a/src/tools/msvc/vcregress.pl
+++ b/src/tools/msvc/vcregress.pl
@@ -179,7 +179,7 @@ sub ecpgcheck
         "../../../../$Config/pg_regress_ecpg/pg_regress_ecpg",
         "--bindir=",
         "--dbname=ecpg1_regression,ecpg2_regression",
-        "--create-role=regress_ecpg_user1,regress_ecpg_user2",
+        "--create-role=regress_ecpg_user1,ecpg2_regression",
         "--schedule=${schedule}_schedule",
         "--encoding=SQL_ASCII",
         "--no-locale",

On Wed, Jun 09, 2021 at 12:05:10PM -0400, Tom Lane wrote:
> Here's a draft patch that renames regress_ecpg_user2 to ecpg2_regression,
> which matches the name of one of the databases used, allowing the test
> cases with defaulted database name to succeed.  That gets rid of one of
> the problematic diffs.

Yeah, I agree that this does not matter much for this one, as we want
to stress the quotes and the grammar for the connections here, as
99a5619 implies.  It is good to check for the failure path as well, so
what you have here looks fine to me.

> As it stood, though, that meant that connect/test5
> wasn't exercising the connection-failure code path at all, which didn't
> seem like what we want.  So I adjusted the second place that had been
> failing to again fail on no-such-database, and stuck in gssencmode=disable
> so that we shouldn't get any test diff on hamerkop.

Using ecpg2_regression for the role goes a bit against the recent rule
to not create any role not suffixed by "regress_" as part of the
regression tests, but I am fine to live with that here.

The changes for test1 with MinGW look right, I have not been able to
test them.
--
Michael

Attachment
Michael Paquier <michael@paquier.xyz> writes:
> On Wed, Jun 09, 2021 at 12:05:10PM -0400, Tom Lane wrote:
>> Here's a draft patch that renames regress_ecpg_user2 to ecpg2_regression,

> Using ecpg2_regression for the role goes a bit against the recent rule
> to not create any role not suffixed by "regress_" as part of the
> regression tests, but I am fine to live with that here.

Oh dear, I forgot to check that carefully.  I'd been thinking the rule was
that such names must *contain* "regress", but looking at user.c, it's
stricter:

#ifdef ENFORCE_REGRESSION_TEST_NAME_RESTRICTIONS
    if (strncmp(stmt->role, "regress_", 8) != 0)
        elog(WARNING, "roles created by regression test cases should have names starting with \"regress_\"");
#endif

Meanwhile, the rule for database names is:

#ifdef ENFORCE_REGRESSION_TEST_NAME_RESTRICTIONS
    if (IsUnderPostmaster && strstr(dbname, "regression") == NULL)
        elog(WARNING, "databases created by regression test cases should have names including \"regression\"");
#endif

So unless we want to relax one or both of those, we can't have a user
name that matches the database name.

Now I'm inclined to go back to the first-draft patch I had, which just
dropped the first problematic test case, and added gssencmode=disable
to the second one.

            regards, tom lane



I wrote:
> Now I'm inclined to go back to the first-draft patch I had, which just
> dropped the first problematic test case, and added gssencmode=disable
> to the second one.

Done that way.  If we figure out why the GSS code is acting strangely
on hamerkop, maybe this can be reverted --- but it seems like we already
spent more time than is justified looking for band-aids.

            regards, tom lane



Michael Paquier <michael@paquier.xyz> writes:
> On Sun, May 30, 2021 at 08:25:00PM -0500, Justin Pryzby wrote:
>> ..But I think it's not useful to put details into errorMessage on success,
>> unless you're going to document that.  It would never have occurred to me to
>> look there, or that it would even be safe.

> Yeah.  On the contrary, it could be confusing if one sees an error
> message but there is nothing to worry about, because things are
> working in the scope of what the user wanted at connection time.

I got around to looking at this issue today, and verified that only one
place needs to be changed, as attached.

Although I was initially thinking that maybe we should leave the code
as-is, I now agree that resetting errorMessage is a good idea, because
what tends to be in it at this point is something like

"connection to server on socket "/tmp/.s.PGSQL.5432" failed: "

(ie the string made by emitHostIdentityInfo).  Anybody who does
look at that is likely to be confused, because the connection
*didn't* fail.

There might be some value in my original idea of preserving a trace of
the servers we tried before succeeding.  But it would take additional
work to present it in a non-confusing way, and given the lack of any
field requests for that, I'm not excited about doing it right now.
(One could also argue that it ought to get tied into the PQtrace
facilities somehow, rather than being done in this ad-hoc way.)

            regards, tom lane

diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 49eec3e835..b288d346f9 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -3654,6 +3654,13 @@ keep_going:                        /* We will come back to here until there is
                 /* We can release the address list now. */
                 release_conn_addrinfo(conn);

+                /*
+                 * Contents of conn->errorMessage are no longer interesting
+                 * (and it seems some clients expect it to be empty after a
+                 * successful connection).
+                 */
+                resetPQExpBuffer(&conn->errorMessage);
+
                 /* We are open for business! */
                 conn->status = CONNECTION_OK;
                 return PGRES_POLLING_OK;

Re: Multiple hosts in connection string failed to failover in non-hot standby mode

From
Michael Paquier
Date:
On Mon, Sep 13, 2021 at 04:09:26PM -0400, Tom Lane wrote:
> I got around to looking at this issue today, and verified that only one
> place needs to be changed, as attached.

Thanks!  This looks fine to me at quick glance.
--
Michael

Attachment