Re: Multiple hosts in connection string failed to failover in non-hot standby mode - Mailing list pgsql-hackers

From Tom Lane
Subject Re: Multiple hosts in connection string failed to failover in non-hot standby mode
Date
Msg-id 810463.1610333794@sss.pgh.pa.us
Whole thread Raw
In response to Re: Multiple hosts in connection string failed to failover in non-hot standby mode  (Tom Lane <tgl@sss.pgh.pa.us>)
Responses Re: Multiple hosts in connection string failed to failover in non-hot standby mode  (Hubert Zhang <zhubert@vmware.com>)
List pgsql-hackers
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 */ );
 }

pgsql-hackers by date:

Previous
From: Tatsuo Ishii
Date:
Subject: Re: Inconsistent "" use
Next
From: Thomas Munro
Date:
Subject: Re: pg_preadv() and pg_pwritev()