Re: libpq should append auth failures, not overwrite

Поиск
Список
Период
Сортировка
От Tom Lane
Тема Re: libpq should append auth failures, not overwrite
Дата
Msg-id 3133.1534199223@sss.pgh.pa.us
обсуждение исходный текст
Ответ на Re: libpq should append auth failures, not overwrite  (Fabien COELHO <coelho@cri.ensmp.fr>)
Ответы Re: libpq should append auth failures, not overwrite
Список pgsql-hackers
Fabien COELHO <coelho@cri.ensmp.fr> writes:
>> As I explained in my comments, the reason I did not do these things
>> is that I didn't want to change the output for cases in which just a
>> single host name is given.  I still don't.

> Ok, I get your argument when there is just one target server (cluster), 
> which is probably at least 99.9% of the use case in practice.
> However, ISTM multiple ip & multiple hostname look pretty close.
> I guess that the number of people that use these features is small, but 
> for these when things go wrong better messages are useful... so I would 
> see no harm to trigger the server introductory line when there are 
> multiples servers, whatever the reason why there are multiples servers.

The thing is that there are a *lot* of systems nowadays on which localhost
maps to both ipv4 and ipv6, so that "host=localhost" would be enough to
trigger the new behavior, and I think that would inevitably give rise to
more complaints than kudos.  So I'm still of the opinion that we're better
off with the second patch's behavior as it stands.

Responding to your other points from upthread:

> There are still 86 instances of "printfPQExpBuffer", which seems like a 
> lot. They are mostly OOM messages, but not only. This make checking the 
> patch quite cumbersome.
> I'd rather there would be only one rule, and clear reset with a comment 
> when needded (eg "fe-lobj.c").

Well, I did this for discussion's sake, but I don't really find the
resulting changes in fe-lobj.c to be any sort of improvement.  In
particular, the messiness around suppressing extraneous complaints from
lo_close is still there, if anything worse than before (it's hard to
get rid of because lo_close will reset the buffer).  I'd rather just
leave fe-lobj.c alone, possibly adding a header comment explaining why
it's different.  Which is basically because none of those functions
expect to be appending multiple errors; they're all gonna quit after
the first one.

> I agree with your suggestion of adding a function trying to preserve 
> messages on OOM errors if possible. Maybe "appendPQExpBufferOOM"?

Yeah, done.

> It is unclear to me why the "printf" variant is used in 
> "PQencryptPasswordConn" and "connectOptions2", it looks that you skipped 
> these functions.

I did because it seemed like unnecessary extra diff content, since
we're expecting the error buffer to be initially empty anyway (for
connectOptions2) or we'd just need an extra reset (for
PQencryptPasswordConn).  In the attached I went ahead and made those
changes, but again I'm unsure that it's really a net improvement.

> In passing, some OOM message are rather terse: "out of memory\n", other 
> are more precise "out of memory allocating GSSAPI buffer (%d)\n".

I got rid of the latter; I think they're unnecessary translation burdens,
and it'd be hard to fit them into a one-size-fits-all OOM reporting
subroutine, and it's pretty unclear what's the point of special-casing
them anyway.

> I do not like the resulting output
> server:
> problem found
>    more details
> I'd rather have
> server: problem found
>    more details

Done in the attached, although I'm concerned about the resulting effects
for error message line length --- in my tests, lines that used to reliably
fit in 80 columns don't anymore.  Again, I'm not really convinced this is
an improvement.  It would absolutely *not* be an improvement if we tried
to also shoehorn the server's IP address in there; that'd make the lines
way too long, with way too much stuff before the important part.

            regards, tom lane

diff --git a/src/interfaces/libpq/fe-auth-scram.c b/src/interfaces/libpq/fe-auth-scram.c
index 1d9c937..e7110d5 100644
--- a/src/interfaces/libpq/fe-auth-scram.c
+++ b/src/interfaces/libpq/fe-auth-scram.c
@@ -185,14 +185,14 @@ pg_fe_scram_exchange(void *opaq, char *input, int inputlen,
     {
         if (inputlen == 0)
         {
-            printfPQExpBuffer(&conn->errorMessage,
-                              libpq_gettext("malformed SCRAM message (empty message)\n"));
+            appendPQExpBufferStr(&conn->errorMessage,
+                                 libpq_gettext("malformed SCRAM message (empty message)\n"));
             goto error;
         }
         if (inputlen != strlen(input))
         {
-            printfPQExpBuffer(&conn->errorMessage,
-                              libpq_gettext("malformed SCRAM message (length mismatch)\n"));
+            appendPQExpBufferStr(&conn->errorMessage,
+                                 libpq_gettext("malformed SCRAM message (length mismatch)\n"));
             goto error;
         }
     }
@@ -240,17 +240,17 @@ pg_fe_scram_exchange(void *opaq, char *input, int inputlen,
             else
             {
                 *success = false;
-                printfPQExpBuffer(&conn->errorMessage,
-                                  libpq_gettext("incorrect server signature\n"));
+                appendPQExpBufferStr(&conn->errorMessage,
+                                     libpq_gettext("incorrect server signature\n"));
             }
             *done = true;
             state->state = FE_SCRAM_FINISHED;
             break;

         default:
-            /* shouldn't happen */
-            printfPQExpBuffer(&conn->errorMessage,
-                              libpq_gettext("invalid SCRAM exchange state\n"));
+            /* shouldn't happen, so we don't translate the msg */
+            appendPQExpBufferStr(&conn->errorMessage,
+                                 "invalid SCRAM exchange state\n");
             goto error;
     }
     return;
@@ -272,7 +272,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;
@@ -281,7 +281,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;
@@ -322,16 +322,15 @@ build_client_first_message(fe_scram_state *state)
      */
     if (!pg_frontend_random(raw_nonce, SCRAM_RAW_NONCE_LEN))
     {
-        printfPQExpBuffer(&conn->errorMessage,
-                          libpq_gettext("could not generate nonce\n"));
+        appendPQExpBufferStr(&conn->errorMessage,
+                             libpq_gettext("could not generate nonce\n"));
         return NULL;
     }

     state->client_nonce = malloc(pg_b64_enc_len(SCRAM_RAW_NONCE_LEN) + 1);
     if (state->client_nonce == NULL)
     {
-        printfPQExpBuffer(&conn->errorMessage,
-                          libpq_gettext("out of memory\n"));
+        pqReportOOM(conn);
         return NULL;
     }
     encoded_len = pg_b64_encode(raw_nonce, SCRAM_RAW_NONCE_LEN, state->client_nonce);
@@ -397,8 +396,7 @@ build_client_first_message(fe_scram_state *state)

 oom_error:
     termPQExpBuffer(&buf);
-    printfPQExpBuffer(&conn->errorMessage,
-                      libpq_gettext("out of memory\n"));
+    pqReportOOM(conn);
     return NULL;
 }

@@ -471,13 +469,13 @@ build_client_final_message(fe_scram_state *state)
 #else
         /*
          * Chose channel binding, but the SSL library doesn't support it.
-         * Shouldn't happen.
+         * Shouldn't happen, so we don't translate the msg.
          */
         termPQExpBuffer(&buf);
-        printfPQExpBuffer(&conn->errorMessage,
-                          "channel binding not supported by this build\n");
+        appendPQExpBufferStr(&conn->errorMessage,
+                             "channel binding not supported by this build\n");
         return NULL;
-#endif    /* HAVE_PGTLS_GET_PEER_CERTIFICATE_HASH */
+#endif                            /* HAVE_PGTLS_GET_PEER_CERTIFICATE_HASH */
     }
 #ifdef HAVE_PGTLS_GET_PEER_CERTIFICATE_HASH
     else if (conn->ssl_in_use)
@@ -519,8 +517,7 @@ build_client_final_message(fe_scram_state *state)

 oom_error:
     termPQExpBuffer(&buf);
-    printfPQExpBuffer(&conn->errorMessage,
-                      libpq_gettext("out of memory\n"));
+    pqReportOOM(conn);
     return NULL;
 }

@@ -539,8 +536,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,
-                          libpq_gettext("out of memory\n"));
+        pqReportOOM(conn);
         return false;
     }

@@ -557,16 +553,15 @@ 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,
-                          libpq_gettext("invalid SCRAM response (nonce mismatch)\n"));
+        appendPQExpBufferStr(&conn->errorMessage,
+                             libpq_gettext("invalid SCRAM response (nonce mismatch)\n"));
         return false;
     }

     state->nonce = strdup(nonce);
     if (state->nonce == NULL)
     {
-        printfPQExpBuffer(&conn->errorMessage,
-                          libpq_gettext("out of memory\n"));
+        pqReportOOM(conn);
         return false;
     }

@@ -579,8 +574,7 @@ read_server_first_message(fe_scram_state *state, char *input)
     state->salt = malloc(pg_b64_dec_len(strlen(encoded_salt)));
     if (state->salt == NULL)
     {
-        printfPQExpBuffer(&conn->errorMessage,
-                          libpq_gettext("out of memory\n"));
+        pqReportOOM(conn);
         return false;
     }
     state->saltlen = pg_b64_decode(encoded_salt,
@@ -596,14 +590,14 @@ read_server_first_message(fe_scram_state *state, char *input)
     state->iterations = strtol(iterations_str, &endptr, 10);
     if (*endptr != '\0' || state->iterations < 1)
     {
-        printfPQExpBuffer(&conn->errorMessage,
-                          libpq_gettext("malformed SCRAM message (invalid iteration count)\n"));
+        appendPQExpBufferStr(&conn->errorMessage,
+                             libpq_gettext("malformed SCRAM message (invalid iteration count)\n"));
         return false;
     }

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

     return true;
 }
@@ -621,8 +615,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,
-                          libpq_gettext("out of memory\n"));
+        pqReportOOM(conn);
         return false;
     }

@@ -632,7 +625,7 @@ read_server_final_message(fe_scram_state *state, char *input)
         char       *errmsg = read_attr_value(&input, 'e',
                                              &conn->errorMessage);

-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("error received from server in SCRAM exchange: %s\n"),
                           errmsg);
         return false;
@@ -648,16 +641,16 @@ read_server_final_message(fe_scram_state *state, char *input)
     }

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

     server_signature_len = pg_b64_decode(encoded_server_signature,
                                          strlen(encoded_server_signature),
                                          state->ServerSignature);
     if (server_signature_len != SCRAM_KEY_LEN)
     {
-        printfPQExpBuffer(&conn->errorMessage,
-                          libpq_gettext("malformed SCRAM message (invalid server signature)\n"));
+        appendPQExpBufferStr(&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 540aba9..69f2efb 100644
--- a/src/interfaces/libpq/fe-auth.c
+++ b/src/interfaces/libpq/fe-auth.c
@@ -81,14 +81,12 @@ pg_GSS_error_int(PQExpBuffer str, const char *mprefix,
 }

 /*
- * GSSAPI errors contain two parts; put both into conn->errorMessage.
+ * GSSAPI errors contain two parts; append both to conn->errorMessage.
  */
 static void
 pg_GSS_error(const char *mprefix, PGconn *conn,
              OM_uint32 maj_stat, OM_uint32 min_stat)
 {
-    resetPQExpBuffer(&conn->errorMessage);
-
     /* Fetch major error codes */
     pg_GSS_error_int(&conn->errorMessage, mprefix, maj_stat, GSS_C_GSS_CODE);

@@ -118,9 +116,7 @@ pg_GSS_continue(PGconn *conn, int payloadlen)
         ginbuf.value = malloc(payloadlen);
         if (!ginbuf.value)
         {
-            printfPQExpBuffer(&conn->errorMessage,
-                              libpq_gettext("out of memory allocating GSSAPI buffer (%d)\n"),
-                              payloadlen);
+            pqReportOOM(conn);
             return STATUS_ERROR;
         }
         if (pqGetnchar(ginbuf.value, payloadlen, conn))
@@ -203,15 +199,15 @@ pg_GSS_startup(PGconn *conn, int payloadlen)

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

     if (conn->gctx)
     {
-        printfPQExpBuffer(&conn->errorMessage,
-                          libpq_gettext("duplicate GSS authentication request\n"));
+        appendPQExpBufferStr(&conn->errorMessage,
+                             libpq_gettext("duplicate GSS authentication request\n"));
         return STATUS_ERROR;
     }

@@ -223,8 +219,7 @@ pg_GSS_startup(PGconn *conn, int payloadlen)
     temp_gbuf.value = (char *) malloc(maxlen);
     if (!temp_gbuf.value)
     {
-        printfPQExpBuffer(&conn->errorMessage,
-                          libpq_gettext("out of memory\n"));
+        pqReportOOM(conn);
         return STATUS_ERROR;
     }
     snprintf(temp_gbuf.value, maxlen, "%s@%s",
@@ -268,10 +263,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);
 }

@@ -299,9 +294,7 @@ pg_SSPI_continue(PGconn *conn, int payloadlen)
         inputbuf = malloc(payloadlen);
         if (!inputbuf)
         {
-            printfPQExpBuffer(&conn->errorMessage,
-                              libpq_gettext("out of memory allocating SSPI buffer (%d)\n"),
-                              payloadlen);
+            pqReportOOM(conn);
             return STATUS_ERROR;
         }
         if (pqGetnchar(inputbuf, payloadlen, conn))
@@ -359,7 +352,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"));
+            pqReportOOM(conn);
             return STATUS_ERROR;
         }
         memcpy(conn->sspictx, &newContext, sizeof(CtxtHandle));
@@ -376,9 +369,11 @@ pg_SSPI_continue(PGconn *conn, int payloadlen)
             /*
              * This should never happen, at least not for Kerberos
              * authentication. Keep check in case it shows up with other
-             * authentication methods later.
+             * authentication methods later.  We intentionally don't translate
+             * the message, since we aren't expecting it to happen.
              */
-            printfPQExpBuffer(&conn->errorMessage, "SSPI returned invalid number of output buffers\n");
+            appendPQExpBufferStr(&conn->errorMessage,
+                                 "SSPI returned invalid number of output buffers\n");
             return STATUS_ERROR;
         }

@@ -418,8 +413,8 @@ pg_SSPI_startup(PGconn *conn, int use_negotiate, int payloadlen)

     if (conn->sspictx)
     {
-        printfPQExpBuffer(&conn->errorMessage,
-                          libpq_gettext("duplicate SSPI authentication request\n"));
+        appendPQExpBufferStr(&conn->errorMessage,
+                             libpq_gettext("duplicate SSPI authentication request\n"));
         return STATUS_ERROR;
     }

@@ -429,7 +424,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"));
+        pqReportOOM(conn);
         return STATUS_ERROR;
     }

@@ -457,14 +452,14 @@ pg_SSPI_startup(PGconn *conn, int use_negotiate, int payloadlen)
      */
     if (!(host && host[0] != '\0'))
     {
-        printfPQExpBuffer(&conn->errorMessage,
-                          libpq_gettext("host name must be specified\n"));
+        appendPQExpBufferStr(&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"));
+        pqReportOOM(conn);
         return STATUS_ERROR;
     }
     sprintf(conn->sspitarget, "%s/%s", conn->krbsrvname, host);
@@ -497,8 +492,8 @@ pg_SASL_init(PGconn *conn, int payloadlen)

     if (conn->sasl_state)
     {
-        printfPQExpBuffer(&conn->errorMessage,
-                          libpq_gettext("duplicate SASL authentication request\n"));
+        appendPQExpBufferStr(&conn->errorMessage,
+                             libpq_gettext("duplicate SASL authentication request\n"));
         goto error;
     }

@@ -513,8 +508,8 @@ pg_SASL_init(PGconn *conn, int payloadlen)
     {
         if (pqGets(&mechanism_buf, conn))
         {
-            printfPQExpBuffer(&conn->errorMessage,
-                              "fe_sendauth: invalid authentication request from server: invalid list of authentication
mechanisms\n");
+            appendPQExpBufferStr(&conn->errorMessage,
+                                 libpq_gettext("invalid authentication request from server: invalid list of
authenticationmechanisms\n")); 
             goto error;
         }
         if (PQExpBufferDataBroken(mechanism_buf))
@@ -545,8 +540,8 @@ 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,
-                                  libpq_gettext("server offered SCRAM-SHA-256-PLUS authentication over a non-SSL
connection\n"));
+                appendPQExpBufferStr(&conn->errorMessage,
+                                     libpq_gettext("server offered SCRAM-SHA-256-PLUS authentication over a non-SSL
connection\n"));
                 goto error;
             }
         }
@@ -557,8 +552,8 @@ pg_SASL_init(PGconn *conn, int payloadlen)

     if (!selected_mechanism)
     {
-        printfPQExpBuffer(&conn->errorMessage,
-                          libpq_gettext("none of the server's SASL authentication mechanisms are supported\n"));
+        appendPQExpBufferStr(&conn->errorMessage,
+                             libpq_gettext("none of the server's SASL authentication mechanisms are supported\n"));
         goto error;
     }

@@ -578,8 +573,9 @@ pg_SASL_init(PGconn *conn, int payloadlen)
         password = conn->pgpass;
     if (password == NULL || password[0] == '\0')
     {
-        printfPQExpBuffer(&conn->errorMessage,
-                          PQnoPasswordSupplied);
+        /* Intentionally not translated. */
+        appendPQExpBufferStr(&conn->errorMessage,
+                             PQnoPasswordSupplied);
         goto error;
     }

@@ -639,8 +635,7 @@ oom_error:
     termPQExpBuffer(&mechanism_buf);
     if (initialresponse)
         free(initialresponse);
-    printfPQExpBuffer(&conn->errorMessage,
-                      libpq_gettext("out of memory\n"));
+    pqReportOOM(conn);
     return STATUS_ERROR;
 }

@@ -663,9 +658,7 @@ pg_SASL_continue(PGconn *conn, int payloadlen, bool final)
     challenge = malloc(payloadlen + 1);
     if (!challenge)
     {
-        printfPQExpBuffer(&conn->errorMessage,
-                          libpq_gettext("out of memory allocating SASL buffer (%d)\n"),
-                          payloadlen);
+        pqReportOOM(conn);
         return STATUS_ERROR;
     }

@@ -688,8 +681,8 @@ pg_SASL_continue(PGconn *conn, int payloadlen, bool final)
         if (outputlen != 0)
             free(output);

-        printfPQExpBuffer(&conn->errorMessage,
-                          libpq_gettext("AuthenticationSASLFinal received from server, but SASL authentication was not
completed\n"));
+        appendPQExpBufferStr(&conn->errorMessage,
+                             libpq_gettext("AuthenticationSASLFinal received from server, but SASL authentication was
notcompleted\n")); 
         return STATUS_ERROR;
     }
     if (outputlen != 0)
@@ -758,15 +751,15 @@ pg_local_sendauth(PGconn *conn)
     {
         char        sebuf[256];

-        printfPQExpBuffer(&conn->errorMessage,
-                          "pg_local_sendauth: sendmsg: %s\n",
+        appendPQExpBuffer(&conn->errorMessage,
+                          libpq_gettext("could not send authentication message: %s\n"),
                           pqStrerror(errno, sebuf, sizeof(sebuf)));
         return STATUS_ERROR;
     }
     return STATUS_OK;
 #else
-    printfPQExpBuffer(&conn->errorMessage,
-                      libpq_gettext("SCM_CRED authentication method not supported\n"));
+    appendPQExpBufferStr(&conn->errorMessage,
+                         libpq_gettext("SCM_CRED authentication method not supported\n"));
     return STATUS_ERROR;
 #endif
 }
@@ -798,8 +791,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,
-                                      libpq_gettext("out of memory\n"));
+                    pqReportOOM(conn);
                     return STATUS_ERROR;
                 }

@@ -856,13 +848,13 @@ pg_fe_sendauth(AuthRequest areq, int payloadlen, PGconn *conn)
             break;

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

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

 #if defined(ENABLE_GSS) || defined(ENABLE_SSPI)
@@ -932,8 +924,8 @@ 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,
-                              libpq_gettext("GSSAPI authentication not supported\n"));
+            appendPQExpBufferStr(&conn->errorMessage,
+                                 libpq_gettext("GSSAPI authentication not supported\n"));
             return STATUS_ERROR;
 #endif                            /* defined(ENABLE_GSS) || defined(ENABLE_SSPI) */

@@ -964,16 +956,16 @@ pg_fe_sendauth(AuthRequest areq, int payloadlen, PGconn *conn)
              */
 #if !defined(ENABLE_GSS)
         case AUTH_REQ_SSPI:
-            printfPQExpBuffer(&conn->errorMessage,
-                              libpq_gettext("SSPI authentication not supported\n"));
+            appendPQExpBufferStr(&conn->errorMessage,
+                                 libpq_gettext("SSPI authentication not supported\n"));
             return STATUS_ERROR;
 #endif                            /* !define(ENABLE_GSSAPI) */
 #endif                            /* ENABLE_SSPI */


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

         case AUTH_REQ_MD5:
@@ -987,14 +979,15 @@ pg_fe_sendauth(AuthRequest areq, int payloadlen, PGconn *conn)
                     password = conn->pgpass;
                 if (password == NULL || password[0] == '\0')
                 {
-                    printfPQExpBuffer(&conn->errorMessage,
-                                      PQnoPasswordSupplied);
+                    /* Intentionally not translated. */
+                    appendPQExpBufferStr(&conn->errorMessage,
+                                         PQnoPasswordSupplied);
                     return STATUS_ERROR;
                 }
                 if (pg_password_sendauth(conn, password, areq) != STATUS_OK)
                 {
-                    printfPQExpBuffer(&conn->errorMessage,
-                                      "fe_sendauth: error sending password authentication\n");
+                    appendPQExpBufferStr(&conn->errorMessage,
+                                         libpq_gettext("error sending password authentication\n"));
                     return STATUS_ERROR;
                 }
                 break;
@@ -1017,17 +1010,15 @@ pg_fe_sendauth(AuthRequest areq, int payloadlen, PGconn *conn)
         case AUTH_REQ_SASL_FIN:
             if (conn->sasl_state == NULL)
             {
-                printfPQExpBuffer(&conn->errorMessage,
-                                  "fe_sendauth: invalid authentication request from server: AUTH_REQ_SASL_CONT without
AUTH_REQ_SASL\n");
+                appendPQExpBufferStr(&conn->errorMessage,
+                                     libpq_gettext("invalid authentication request from server: AUTH_REQ_SASL_CONT
withoutAUTH_REQ_SASL\n")); 
                 return STATUS_ERROR;
             }
             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,
-                                      "fe_sendauth: error in SASL authentication\n");
+                appendPQExpBufferStr(&conn->errorMessage,
+                                     libpq_gettext("error in SASL authentication\n"));
                 return STATUS_ERROR;
             }
             break;
@@ -1038,7 +1029,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;
     }
@@ -1052,7 +1043,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)
@@ -1085,7 +1076,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
@@ -1095,12 +1086,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,
                               pqStrerror(pwerr, pwdbuf, sizeof(pwdbuf)));
         else
-            printfPQExpBuffer(errorMessage,
+            appendPQExpBuffer(errorMessage,
                               libpq_gettext("local user with ID %d does not exist\n"),
                               (int) user_id);
     }
@@ -1110,8 +1101,7 @@ pg_fe_getauthname(PQExpBuffer errorMessage)
     {
         result = strdup(name);
         if (result == NULL && errorMessage)
-            printfPQExpBuffer(errorMessage,
-                              libpq_gettext("out of memory\n"));
+            pqReportOOMBuffer(errorMessage);
     }

     pgunlock_thread();
@@ -1181,12 +1171,16 @@ PQencryptPasswordConn(PGconn *conn, const char *passwd, const char *user,
     if (!conn)
         return NULL;

+    /* This is a library entry point, so start with an empty error message. */
+    resetPQExpBuffer(&conn->errorMessage);
+
     /* If no algorithm was given, ask the server. */
     if (algorithm == NULL)
     {
         PGresult   *res;
         char       *val;

+        /* PQexec will reset errorMessage again, but that's OK */
         res = PQexec(conn, "show password_encryption");
         if (res == NULL)
         {
@@ -1202,8 +1196,8 @@ PQencryptPasswordConn(PGconn *conn, const char *passwd, const char *user,
         if (PQntuples(res) != 1 || PQnfields(res) != 1)
         {
             PQclear(res);
-            printfPQExpBuffer(&conn->errorMessage,
-                              libpq_gettext("unexpected shape of result set returned for SHOW\n"));
+            appendPQExpBufferStr(&conn->errorMessage,
+                                 libpq_gettext("unexpected shape of result set returned for SHOW\n"));
             return NULL;
         }
         val = PQgetvalue(res, 0, 0);
@@ -1211,8 +1205,8 @@ PQencryptPasswordConn(PGconn *conn, const char *passwd, const char *user,
         if (strlen(val) > MAX_ALGORITHM_NAME_LEN)
         {
             PQclear(res);
-            printfPQExpBuffer(&conn->errorMessage,
-                              libpq_gettext("password_encryption value too long\n"));
+            appendPQExpBufferStr(&conn->errorMessage,
+                                 libpq_gettext("password_encryption value too long\n"));
             return NULL;
         }
         strcpy(algobuf, val);
@@ -1251,15 +1245,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,
-                          libpq_gettext("out of memory\n"));
+        pqReportOOM(conn);

     return crypt_pwd;
 }
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 9315a27..647423c 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -831,8 +831,7 @@ fillPGconn(PGconn *conn, PQconninfoOption *connOptions)
                 *connmember = strdup(tmp);
                 if (*connmember == NULL)
                 {
-                    printfPQExpBuffer(&conn->errorMessage,
-                                      libpq_gettext("out of memory\n"));
+                    pqReportOOM(conn);
                     return false;
                 }
             }
@@ -1014,7 +1013,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;
@@ -1090,7 +1089,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;
@@ -1186,7 +1185,7 @@ connectOptions2(PGconn *conn)
             && strcmp(conn->sslmode, "verify-full") != 0)
         {
             conn->status = CONNECTION_BAD;
-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("invalid sslmode value: \"%s\"\n"),
                               conn->sslmode);
             return false;
@@ -1207,7 +1206,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;
@@ -1242,7 +1241,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 target_session_attrs value: \"%s\"\n"),
                               conn->target_session_attrs);
             return false;
@@ -1260,8 +1259,7 @@ connectOptions2(PGconn *conn)

 oom_error:
     conn->status = CONNECTION_BAD;
-    printfPQExpBuffer(&conn->errorMessage,
-                      libpq_gettext("out of memory\n"));
+    pqReportOOM(conn);
     return false;
 }

@@ -1436,8 +1434,7 @@ PQsetdbLogin(const char *pghost, const char *pgport, const char *pgoptions,

 oom_error:
     conn->status = CONNECTION_BAD;
-    printfPQExpBuffer(&conn->errorMessage,
-                      libpq_gettext("out of memory\n"));
+    pqReportOOM(conn);
     return conn;
 }

@@ -1757,10 +1754,10 @@ connectDBStart(PGconn *conn)
     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.
+     * Ensure errorMessage is empty, too.  PQconnectPoll and its subroutines
+     * 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);

@@ -1955,12 +1952,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:
@@ -2005,46 +1996,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
  *
@@ -2080,7 +2031,6 @@ PQconnectPoll(PGconn *conn)
     PGresult   *res;
     char        sebuf[256];
     int            optval;
-    PQExpBufferData savedMessage;

     if (conn == NULL)
         return PGRES_POLLING_FAILED;
@@ -2601,12 +2551,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,
-                                      libpq_gettext("out of memory\n"));
+                    pqReportOOM(conn);
                     goto error_return;
                 }

@@ -2992,7 +2937,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;
@@ -3112,31 +3056,19 @@ keep_going:                        /* We will come back to here until there is
                     conn->target_session_attrs != NULL &&
                     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.
-                     */
-                    if (!saveErrorMessage(conn, &savedMessage))
-                        goto error_return;
-
-                    conn->status = CONNECTION_OK;
                     if (!PQsendQuery(conn,
                                      "SHOW transaction_read_only"))
-                    {
-                        restoreErrorMessage(conn, &savedMessage);
                         goto error_return;
-                    }
                     conn->status = CONNECTION_CHECK_WRITABLE;
-                    restoreErrorMessage(conn, &savedMessage);
                     return PGRES_POLLING_READING;
                 }

                 /* We can release the address lists now. */
                 release_all_addrinfo(conn);

+                /* Likewise drop any stored error messages. */
+                resetPQExpBuffer(&conn->errorMessage);
+
                 /* We are open for business! */
                 conn->status = CONNECTION_OK;
                 return PGRES_POLLING_OK;
@@ -3146,23 +3078,16 @@ keep_going:                        /* We will come back to here until there is

             /*
              * Do post-connection housekeeping (only needed in protocol 2.0).
-             *
-             * We pretend that the connection is OK for the duration of these
-             * queries.
              */
-            conn->status = CONNECTION_OK;
-
             switch (pqSetenvPoll(conn))
             {
                 case PGRES_POLLING_OK:    /* Success */
                     break;

                 case PGRES_POLLING_READING: /* Still going */
-                    conn->status = CONNECTION_SETENV;
                     return PGRES_POLLING_READING;

                 case PGRES_POLLING_WRITING: /* Still going */
-                    conn->status = CONNECTION_SETENV;
                     return PGRES_POLLING_WRITING;

                 default:
@@ -3182,39 +3107,30 @@ keep_going:                        /* We will come back to here until there is
                 conn->target_session_attrs != NULL &&
                 strcmp(conn->target_session_attrs, "read-write") == 0)
             {
-                if (!saveErrorMessage(conn, &savedMessage))
-                    goto error_return;
-
-                conn->status = CONNECTION_OK;
                 if (!PQsendQuery(conn,
                                  "SHOW transaction_read_only"))
-                {
-                    restoreErrorMessage(conn, &savedMessage);
                     goto error_return;
-                }
                 conn->status = CONNECTION_CHECK_WRITABLE;
-                restoreErrorMessage(conn, &savedMessage);
                 return PGRES_POLLING_READING;
             }

             /* We can release the address lists now. */
             release_all_addrinfo(conn);

+            /* Likewise drop any stored error messages. */
+            resetPQExpBuffer(&conn->errorMessage);
+
             /* We are open for business! */
             conn->status = CONNECTION_OK;
             return PGRES_POLLING_OK;

         case CONNECTION_CONSUME:
             {
-                conn->status = CONNECTION_OK;
                 if (!PQconsumeInput(conn))
                     goto error_return;

                 if (PQisBusy(conn))
-                {
-                    conn->status = CONNECTION_CONSUME;
                     return PGRES_POLLING_READING;
-                }

                 /*
                  * Call PQgetResult() again to consume NULL result.
@@ -3223,10 +3139,15 @@ keep_going:                        /* We will come back to here until there is
                 if (res != NULL)
                 {
                     PQclear(res);
-                    conn->status = CONNECTION_CONSUME;
                     goto keep_going;
                 }

+                /* We can release the address lists now. */
+                release_all_addrinfo(conn);
+
+                /* Likewise drop any stored error messages. */
+                resetPQExpBuffer(&conn->errorMessage);
+
                 /* We are open for business! */
                 conn->status = CONNECTION_OK;
                 return PGRES_POLLING_OK;
@@ -3236,22 +3157,11 @@ 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;
-                }

                 res = PQgetResult(conn);
                 if (res && (PQresultStatus(res) == PGRES_TUPLES_OK) &&
@@ -3267,7 +3177,6 @@ keep_going:                        /* We will come back to here until there is
                         const char *displayed_port;

                         PQclear(res);
-                        restoreErrorMessage(conn, &savedMessage);

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

                     /* Session is read-write, so we're good. */
                     PQclear(res);
-                    termPQExpBuffer(&savedMessage);
-
-                    /* We can release the address lists now. */
-                    release_all_addrinfo(conn);

                     /*
                      * Finish reading any remaining messages before being
@@ -3317,7 +3222,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)
@@ -3771,7 +3675,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;
@@ -3831,7 +3735,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;
@@ -4166,7 +4070,7 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options,

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

@@ -4178,8 +4082,9 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options,

     if (pg_strncasecmp(url, LDAP_URL, strlen(LDAP_URL)) != 0)
     {
-        printfPQExpBuffer(errorMessage,
-                          libpq_gettext("invalid LDAP URL \"%s\": scheme must be ldap://\n"), purl);
+        appendPQExpBuffer(errorMessage,
+                          libpq_gettext("invalid LDAP URL \"%s\": scheme must be ldap://\n"),
+                          purl);
         free(url);
         return 3;
     }
@@ -4193,8 +4098,9 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options,
     p = strchr(url + strlen(LDAP_URL), '/');
     if (p == NULL || *(p + 1) == '\0' || *(p + 1) == '?')
     {
-        printfPQExpBuffer(errorMessage, libpq_gettext(
-                                                      "invalid LDAP URL \"%s\": missing distinguished name\n"), purl);
+        appendPQExpBuffer(errorMessage,
+                          libpq_gettext("invalid LDAP URL \"%s\": missing distinguished name\n"),
+                          purl);
         free(url);
         return 3;
     }
@@ -4204,8 +4110,9 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options,
     /* attribute */
     if ((p = strchr(dn, '?')) == NULL || *(p + 1) == '\0' || *(p + 1) == '?')
     {
-        printfPQExpBuffer(errorMessage, libpq_gettext(
-                                                      "invalid LDAP URL \"%s\": must have exactly one attribute\n"),
purl);
+        appendPQExpBuffer(errorMessage,
+                          libpq_gettext("invalid LDAP URL \"%s\": must have exactly one attribute\n"),
+                          purl);
         free(url);
         return 3;
     }
@@ -4215,7 +4122,9 @@ 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;
     }
@@ -4225,7 +4134,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;
@@ -4246,8 +4155,9 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options,
         lport = strtol(portstr, &endptr, 10);
         if (*portstr == '\0' || *endptr != '\0' || errno || lport < 0 || lport > 65535)
         {
-            printfPQExpBuffer(errorMessage, libpq_gettext(
-                                                          "invalid LDAP URL \"%s\": invalid port number\n"), purl);
+            appendPQExpBuffer(errorMessage,
+                              libpq_gettext("invalid LDAP URL \"%s\": invalid port number\n"),
+                              purl);
             free(url);
             return 3;
         }
@@ -4257,8 +4167,9 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options,
     /* Allow only one attribute */
     if (strchr(attrs[0], ',') != NULL)
     {
-        printfPQExpBuffer(errorMessage, libpq_gettext(
-                                                      "invalid LDAP URL \"%s\": must have exactly one attribute\n"),
purl);
+        appendPQExpBuffer(errorMessage,
+                          libpq_gettext("invalid LDAP URL \"%s\": must have exactly one attribute\n"),
+                          purl);
         free(url);
         return 3;
     }
@@ -4272,7 +4183,9 @@ 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;
     }
@@ -4280,8 +4193,8 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options,
     /* initialize LDAP structure */
     if ((ld = ldap_init(hostname, port)) == NULL)
     {
-        printfPQExpBuffer(errorMessage,
-                          libpq_gettext("could not create LDAP structure\n"));
+        appendPQExpBufferStr(errorMessage,
+                             libpq_gettext("could not create LDAP structure\n"));
         free(url);
         return 3;
     }
@@ -4356,7 +4269,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);
@@ -4367,9 +4280,9 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options,
     /* complain if there was not exactly one result */
     if ((rc = ldap_count_entries(ld, res)) != 1)
     {
-        printfPQExpBuffer(errorMessage,
-                          rc ? libpq_gettext("more than one entry found on LDAP lookup\n")
-                          : libpq_gettext("no entry found on LDAP lookup\n"));
+        appendPQExpBufferStr(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);
         ldap_unbind(ld);
         free(url);
@@ -4380,8 +4293,8 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options,
     if ((entry = ldap_first_entry(ld, res)) == NULL)
     {
         /* should never happen */
-        printfPQExpBuffer(errorMessage,
-                          libpq_gettext("no entry found on LDAP lookup\n"));
+        appendPQExpBufferStr(errorMessage,
+                             libpq_gettext("no entry found on LDAP lookup\n"));
         ldap_msgfree(res);
         ldap_unbind(ld);
         free(url);
@@ -4391,8 +4304,8 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options,
     /* get values */
     if ((values = ldap_get_values_len(ld, entry, attrs[0])) == NULL)
     {
-        printfPQExpBuffer(errorMessage,
-                          libpq_gettext("attribute has no values on LDAP lookup\n"));
+        appendPQExpBufferStr(errorMessage,
+                             libpq_gettext("attribute has no values on LDAP lookup\n"));
         ldap_msgfree(res);
         ldap_unbind(ld);
         free(url);
@@ -4404,8 +4317,8 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options,

     if (values[0] == NULL)
     {
-        printfPQExpBuffer(errorMessage,
-                          libpq_gettext("attribute has no values on LDAP lookup\n"));
+        appendPQExpBufferStr(errorMessage,
+                             libpq_gettext("attribute has no values on LDAP lookup\n"));
         ldap_value_free_len(values);
         ldap_unbind(ld);
         return 1;
@@ -4417,10 +4330,9 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options,
         size += values[i]->bv_len + 1;
     if ((result = malloc(size)) == NULL)
     {
-        printfPQExpBuffer(errorMessage,
-                          libpq_gettext("out of memory\n"));
         ldap_value_free_len(values);
         ldap_unbind(ld);
+        pqReportOOMBuffer(errorMessage);
         return 3;
     }
     p = result;
@@ -4456,8 +4368,8 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options,
                 }
                 else if (ld_is_nl_cr(*p))
                 {
-                    printfPQExpBuffer(errorMessage, libpq_gettext(
-                                                                  "missing \"=\" after \"%s\" in connection info
string\n"),
+                    appendPQExpBuffer(errorMessage,
+                                      libpq_gettext("missing \"=\" after \"%s\" in connection info string\n"),
                                       optname);
                     free(result);
                     return 3;
@@ -4475,8 +4387,8 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options,
                 }
                 else if (!ld_is_sp_tab(*p))
                 {
-                    printfPQExpBuffer(errorMessage, libpq_gettext(
-                                                                  "missing \"=\" after \"%s\" in connection info
string\n"),
+                    appendPQExpBuffer(errorMessage,
+                                      libpq_gettext("missing \"=\" after \"%s\" in connection info string\n"),
                                       optname);
                     free(result);
                     return 3;
@@ -4536,9 +4448,8 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options,
                         options[i].val = strdup(optval);
                         if (!options[i].val)
                         {
-                            printfPQExpBuffer(errorMessage,
-                                              libpq_gettext("out of memory\n"));
                             free(result);
+                            pqReportOOMBuffer(errorMessage);
                             return 3;
                         }
                     }
@@ -4548,7 +4459,7 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options,
             }
             if (!found_keyword)
             {
-                printfPQExpBuffer(errorMessage,
+                appendPQExpBuffer(errorMessage,
                                   libpq_gettext("invalid connection option \"%s\"\n"),
                                   optname);
                 free(result);
@@ -4564,8 +4475,8 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options,

     if (state == 5 || state == 6)
     {
-        printfPQExpBuffer(errorMessage, libpq_gettext(
-                                                      "unterminated quoted string in connection info string\n"));
+        appendPQExpBufferStr(errorMessage,
+                             libpq_gettext("unterminated quoted string in connection info string\n"));
         return 3;
     }

@@ -4583,8 +4494,8 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options,
  * Returns 0 on success, nonzero on failure.  On failure, if errorMessage
  * isn't null, also store an error message there.  (Note: the only reason
  * this function and related ones don't dump core on errorMessage == NULL
- * is the undocumented fact that printfPQExpBuffer does nothing when passed
- * a null PQExpBuffer pointer.)
+ * is the undocumented fact that printfPQExpBuffer and friends do nothing
+ * when passed a null PQExpBuffer pointer.)
  */
 static int
 parseServiceInfo(PQconninfoOption *options, PQExpBuffer errorMessage)
@@ -4647,7 +4558,7 @@ next_file:
 last_file:
     if (!group_found)
     {
-        printfPQExpBuffer(errorMessage,
+        appendPQExpBuffer(errorMessage,
                           libpq_gettext("definition of service \"%s\" not found\n"), service);
         return 3;
     }
@@ -4671,7 +4582,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;
     }
@@ -4683,7 +4594,7 @@ parseServiceFile(const char *serviceFile,
         if (strlen(line) >= sizeof(buf) - 1)
         {
             fclose(f);
-            printfPQExpBuffer(errorMessage,
+            appendPQExpBuffer(errorMessage,
                               libpq_gettext("line %d too long in service file \"%s\"\n"),
                               linenr,
                               serviceFile);
@@ -4754,7 +4665,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);
@@ -4765,7 +4676,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);
@@ -4786,9 +4697,8 @@ parseServiceFile(const char *serviceFile,
                             options[i].val = strdup(val);
                         if (!options[i].val)
                         {
-                            printfPQExpBuffer(errorMessage,
-                                              libpq_gettext("out of memory\n"));
                             fclose(f);
+                            pqReportOOMBuffer(errorMessage);
                             return 3;
                         }
                         found_keyword = true;
@@ -4798,7 +4708,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);
@@ -4842,7 +4752,7 @@ PQconninfoParse(const char *conninfo, char **errmsg)
     if (PQExpBufferDataBroken(errorBuf))
         return NULL;            /* out of memory already :-( */
     connOptions = parse_connection_string(conninfo, &errorBuf, false);
-    if (connOptions == NULL && errmsg)
+    if (connOptions == NULL && errmsg && !PQExpBufferDataBroken(errorBuf))
         *errmsg = errorBuf.data;
     else
         termPQExpBuffer(&errorBuf);
@@ -4866,8 +4776,7 @@ conninfo_init(PQExpBuffer errorMessage)
     options = (PQconninfoOption *) malloc(sizeof(PQconninfoOption) * sizeof(PQconninfoOptions) /
sizeof(PQconninfoOptions[0]));
     if (options == NULL)
     {
-        printfPQExpBuffer(errorMessage,
-                          libpq_gettext("out of memory\n"));
+        pqReportOOMBuffer(errorMessage);
         return NULL;
     }
     opt_dest = options;
@@ -4965,9 +4874,8 @@ conninfo_parse(const char *conninfo, PQExpBuffer errorMessage,
     /* Need a modifiable copy of the input string */
     if ((buf = strdup(conninfo)) == NULL)
     {
-        printfPQExpBuffer(errorMessage,
-                          libpq_gettext("out of memory\n"));
         PQconninfoFree(options);
+        pqReportOOMBuffer(errorMessage);
         return NULL;
     }
     cp = buf;
@@ -5004,7 +4912,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);
@@ -5053,8 +4961,8 @@ conninfo_parse(const char *conninfo, PQExpBuffer errorMessage,
             {
                 if (*cp == '\0')
                 {
-                    printfPQExpBuffer(errorMessage,
-                                      libpq_gettext("unterminated quoted string in connection info string\n"));
+                    appendPQExpBufferStr(errorMessage,
+                                         libpq_gettext("unterminated quoted string in connection info string\n"));
                     PQconninfoFree(options);
                     free(buf);
                     return NULL;
@@ -5189,7 +5097,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);
@@ -5221,10 +5129,9 @@ conninfo_array_parse(const char *const *keywords, const char *const *values,
                                 options[k].val = strdup(str_option->val);
                                 if (!options[k].val)
                                 {
-                                    printfPQExpBuffer(errorMessage,
-                                                      libpq_gettext("out of memory\n"));
                                     PQconninfoFree(options);
                                     PQconninfoFree(dbname_options);
+                                    pqReportOOMBuffer(errorMessage);
                                     return NULL;
                                 }
                                 break;
@@ -5250,10 +5157,9 @@ conninfo_array_parse(const char *const *keywords, const char *const *values,
                 option->val = strdup(pvalue);
                 if (!option->val)
                 {
-                    printfPQExpBuffer(errorMessage,
-                                      libpq_gettext("out of memory\n"));
                     PQconninfoFree(options);
                     PQconninfoFree(dbname_options);
+                    pqReportOOMBuffer(errorMessage);
                     return NULL;
                 }
             }
@@ -5322,8 +5228,7 @@ conninfo_add_defaults(PQconninfoOption *options, PQExpBuffer errorMessage)
                 if (!option->val)
                 {
                     if (errorMessage)
-                        printfPQExpBuffer(errorMessage,
-                                          libpq_gettext("out of memory\n"));
+                        pqReportOOMBuffer(errorMessage);
                     return false;
                 }
                 continue;
@@ -5346,8 +5251,7 @@ conninfo_add_defaults(PQconninfoOption *options, PQExpBuffer errorMessage)
                 if (!option->val)
                 {
                     if (errorMessage)
-                        printfPQExpBuffer(errorMessage,
-                                          libpq_gettext("out of memory\n"));
+                        pqReportOOMBuffer(errorMessage);
                     return false;
                 }
                 continue;
@@ -5364,8 +5268,7 @@ conninfo_add_defaults(PQconninfoOption *options, PQExpBuffer errorMessage)
             if (!option->val)
             {
                 if (errorMessage)
-                    printfPQExpBuffer(errorMessage,
-                                      libpq_gettext("out of memory\n"));
+                    pqReportOOMBuffer(errorMessage);
                 return false;
             }
             continue;
@@ -5465,8 +5368,7 @@ conninfo_uri_parse_options(PQconninfoOption *options, const char *uri,
     initPQExpBuffer(&portbuf);
     if (PQExpBufferDataBroken(hostbuf) || PQExpBufferDataBroken(portbuf))
     {
-        printfPQExpBuffer(errorMessage,
-                          libpq_gettext("out of memory\n"));
+        pqReportOOMBuffer(errorMessage);
         goto cleanup;
     }

@@ -5474,8 +5376,7 @@ conninfo_uri_parse_options(PQconninfoOption *options, const char *uri,
     buf = strdup(uri);
     if (buf == NULL)
     {
-        printfPQExpBuffer(errorMessage,
-                          libpq_gettext("out of memory\n"));
+        pqReportOOMBuffer(errorMessage);
         goto cleanup;
     }
     start = buf;
@@ -5485,7 +5386,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;
@@ -5562,14 +5463,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;
@@ -5584,7 +5485,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;
@@ -5713,7 +5614,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;
@@ -5733,7 +5634,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;
@@ -5784,7 +5685,7 @@ conninfo_uri_parse_params(char *params,
         {
             /* Insert generic message if conninfo_storeval didn't give one. */
             if (errorMessage->len == 0)
-                printfPQExpBuffer(errorMessage,
+                appendPQExpBuffer(errorMessage,
                                   libpq_gettext("invalid URI query parameter: \"%s\"\n"),
                                   keyword);
             /* And fail. */
@@ -5831,7 +5732,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"));
+        pqReportOOMBuffer(errorMessage);
         return NULL;
     }
     p = buf;
@@ -5858,7 +5759,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);
@@ -5868,7 +5769,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);
@@ -5963,7 +5864,7 @@ conninfo_storeval(PQconninfoOption *connOptions,
     if (option == NULL)
     {
         if (!ignoreMissing)
-            printfPQExpBuffer(errorMessage,
+            appendPQExpBuffer(errorMessage,
                               libpq_gettext("invalid connection option \"%s\"\n"),
                               keyword);
         return NULL;
@@ -5981,7 +5882,7 @@ conninfo_storeval(PQconninfoOption *connOptions,
         value_copy = strdup(value);
         if (value_copy == NULL)
         {
-            printfPQExpBuffer(errorMessage, libpq_gettext("out of memory\n"));
+            pqReportOOMBuffer(errorMessage);
             return NULL;
         }
     }
diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c
index 4c0114c..2c1baa7 100644
--- a/src/interfaces/libpq/fe-exec.c
+++ b/src/interfaces/libpq/fe-exec.c
@@ -770,8 +770,7 @@ pqSaveErrorResult(PGconn *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)
@@ -781,21 +780,11 @@ pqPrepareAsyncResult(PGconn *conn)
     /*
      * conn->result is the PGresult to return.  If it is NULL (which probably
      * shouldn't happen) we assume there is an appropriate error message in
-     * conn->errorMessage.
+     * conn->errorMessage, and copy that into a PGresult.
      */
     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
@@ -1188,8 +1177,8 @@ PQsendQuery(PGconn *conn, const char *query)
     /* check the argument */
     if (!query)
     {
-        printfPQExpBuffer(&conn->errorMessage,
-                          libpq_gettext("command string is a null pointer\n"));
+        appendPQExpBufferStr(&conn->errorMessage,
+                             libpq_gettext("command string is a null pointer\n"));
         return 0;
     }

@@ -1246,14 +1235,14 @@ PQsendQueryParams(PGconn *conn,
     /* check the arguments */
     if (!command)
     {
-        printfPQExpBuffer(&conn->errorMessage,
-                          libpq_gettext("command string is a null pointer\n"));
+        appendPQExpBufferStr(&conn->errorMessage,
+                             libpq_gettext("command string is a null pointer\n"));
         return 0;
     }
     if (nParams < 0 || nParams > 65535)
     {
-        printfPQExpBuffer(&conn->errorMessage,
-                          libpq_gettext("number of parameters must be between 0 and 65535\n"));
+        appendPQExpBufferStr(&conn->errorMessage,
+                             libpq_gettext("number of parameters must be between 0 and 65535\n"));
         return 0;
     }

@@ -1286,28 +1275,28 @@ PQsendPrepare(PGconn *conn,
     /* check the arguments */
     if (!stmtName)
     {
-        printfPQExpBuffer(&conn->errorMessage,
-                          libpq_gettext("statement name is a null pointer\n"));
+        appendPQExpBufferStr(&conn->errorMessage,
+                             libpq_gettext("statement name is a null pointer\n"));
         return 0;
     }
     if (!query)
     {
-        printfPQExpBuffer(&conn->errorMessage,
-                          libpq_gettext("command string is a null pointer\n"));
+        appendPQExpBufferStr(&conn->errorMessage,
+                             libpq_gettext("command string is a null pointer\n"));
         return 0;
     }
     if (nParams < 0 || nParams > 65535)
     {
-        printfPQExpBuffer(&conn->errorMessage,
-                          libpq_gettext("number of parameters must be between 0 and 65535\n"));
+        appendPQExpBufferStr(&conn->errorMessage,
+                             libpq_gettext("number of parameters must be between 0 and 65535\n"));
         return 0;
     }

     /* This isn't gonna work on a 2.0 server */
     if (PG_PROTOCOL_MAJOR(conn->pversion) < 3)
     {
-        printfPQExpBuffer(&conn->errorMessage,
-                          libpq_gettext("function requires at least protocol version 3.0\n"));
+        appendPQExpBufferStr(&conn->errorMessage,
+                             libpq_gettext("function requires at least protocol version 3.0\n"));
         return 0;
     }

@@ -1387,14 +1376,14 @@ PQsendQueryPrepared(PGconn *conn,
     /* check the arguments */
     if (!stmtName)
     {
-        printfPQExpBuffer(&conn->errorMessage,
-                          libpq_gettext("statement name is a null pointer\n"));
+        appendPQExpBufferStr(&conn->errorMessage,
+                             libpq_gettext("statement name is a null pointer\n"));
         return 0;
     }
     if (nParams < 0 || nParams > 65535)
     {
-        printfPQExpBuffer(&conn->errorMessage,
-                          libpq_gettext("number of parameters must be between 0 and 65535\n"));
+        appendPQExpBufferStr(&conn->errorMessage,
+                             libpq_gettext("number of parameters must be between 0 and 65535\n"));
         return 0;
     }

@@ -1418,21 +1407,32 @@ PQsendQueryStart(PGconn *conn)
     if (!conn)
         return false;

-    /* clear the error string */
-    resetPQExpBuffer(&conn->errorMessage);
+    /*
+     * Clear the error string at start of a new query.  All libpq code
+     * executed while we're running a query should append messages to
+     * errorMessage.
+     *
+     * However, if we're in a connecting state (anything but CONNECTION_OK or
+     * CONNECTION_BAD), then we don't want to clear the errorMessage.  We must
+     * be doing some libpq-generated query to check server state, and we want
+     * to continue building up the history of connection attempts across
+     * multiple servers --- see comments in connectDBStart.
+     */
+    if (conn->status <= CONNECTION_BAD)
+        resetPQExpBuffer(&conn->errorMessage);

     /* Don't try to send if we know there's no live connection. */
-    if (conn->status != CONNECTION_OK)
+    if (conn->status == CONNECTION_BAD)
     {
-        printfPQExpBuffer(&conn->errorMessage,
-                          libpq_gettext("no connection to the server\n"));
+        appendPQExpBufferStr(&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,
-                          libpq_gettext("another command is already in progress\n"));
+        appendPQExpBufferStr(&conn->errorMessage,
+                             libpq_gettext("another command is already in progress\n"));
         return false;
     }

@@ -1469,8 +1469,8 @@ PQsendQueryGuts(PGconn *conn,
     /* This isn't gonna work on a 2.0 server */
     if (PG_PROTOCOL_MAJOR(conn->pversion) < 3)
     {
-        printfPQExpBuffer(&conn->errorMessage,
-                          libpq_gettext("function requires at least protocol version 3.0\n"));
+        appendPQExpBufferStr(&conn->errorMessage,
+                             libpq_gettext("function requires at least protocol version 3.0\n"));
         return 0;
     }

@@ -1545,8 +1545,8 @@ PQsendQueryGuts(PGconn *conn,
                     nbytes = paramLengths[i];
                 else
                 {
-                    printfPQExpBuffer(&conn->errorMessage,
-                                      libpq_gettext("length must be given for binary parameter\n"));
+                    appendPQExpBufferStr(&conn->errorMessage,
+                                         libpq_gettext("length must be given for binary parameter\n"));
                     goto sendFailed;
                 }
             }
@@ -1817,7 +1817,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);
@@ -1837,7 +1837,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);
@@ -2005,8 +2005,8 @@ PQexecStart(PGconn *conn)
             else
             {
                 /* In older protocols we have to punt */
-                printfPQExpBuffer(&conn->errorMessage,
-                                  libpq_gettext("COPY IN state must be terminated first\n"));
+                appendPQExpBufferStr(&conn->errorMessage,
+                                     libpq_gettext("COPY IN state must be terminated first\n"));
                 return false;
             }
         }
@@ -2025,16 +2025,16 @@ PQexecStart(PGconn *conn)
             else
             {
                 /* In older protocols we have to punt */
-                printfPQExpBuffer(&conn->errorMessage,
-                                  libpq_gettext("COPY OUT state must be terminated first\n"));
+                appendPQExpBufferStr(&conn->errorMessage,
+                                     libpq_gettext("COPY OUT state must be terminated first\n"));
                 return false;
             }
         }
         else if (resultStatus == PGRES_COPY_BOTH)
         {
             /* We don't allow PQexec during COPY BOTH */
-            printfPQExpBuffer(&conn->errorMessage,
-                              libpq_gettext("PQexec not allowed during COPY BOTH\n"));
+            appendPQExpBufferStr(&conn->errorMessage,
+                                 libpq_gettext("PQexec not allowed during COPY BOTH\n"));
             return false;
         }
         /* check for loss of connection, too */
@@ -2076,12 +2076,6 @@ PQexecFinish(PGconn *conn)
                 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);
@@ -2187,8 +2181,8 @@ PQsendDescribe(PGconn *conn, char desc_type, const char *desc_target)
     /* This isn't gonna work on a 2.0 server */
     if (PG_PROTOCOL_MAJOR(conn->pversion) < 3)
     {
-        printfPQExpBuffer(&conn->errorMessage,
-                          libpq_gettext("function requires at least protocol version 3.0\n"));
+        appendPQExpBufferStr(&conn->errorMessage,
+                             libpq_gettext("function requires at least protocol version 3.0\n"));
         return 0;
     }

@@ -2276,8 +2270,8 @@ PQputCopyData(PGconn *conn, const char *buffer, int nbytes)
     if (conn->asyncStatus != PGASYNC_COPY_IN &&
         conn->asyncStatus != PGASYNC_COPY_BOTH)
     {
-        printfPQExpBuffer(&conn->errorMessage,
-                          libpq_gettext("no COPY in progress\n"));
+        appendPQExpBufferStr(&conn->errorMessage,
+                             libpq_gettext("no COPY in progress\n"));
         return -1;
     }

@@ -2343,8 +2337,8 @@ PQputCopyEnd(PGconn *conn, const char *errormsg)
     if (conn->asyncStatus != PGASYNC_COPY_IN &&
         conn->asyncStatus != PGASYNC_COPY_BOTH)
     {
-        printfPQExpBuffer(&conn->errorMessage,
-                          libpq_gettext("no COPY in progress\n"));
+        appendPQExpBufferStr(&conn->errorMessage,
+                             libpq_gettext("no COPY in progress\n"));
         return -1;
     }

@@ -2386,8 +2380,8 @@ PQputCopyEnd(PGconn *conn, const char *errormsg)
         if (errormsg)
         {
             /* Oops, no way to do this in 2.0 */
-            printfPQExpBuffer(&conn->errorMessage,
-                              libpq_gettext("function requires at least protocol version 3.0\n"));
+            appendPQExpBufferStr(&conn->errorMessage,
+                                 libpq_gettext("function requires at least protocol version 3.0\n"));
             return -1;
         }
         else
@@ -2405,7 +2399,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)
@@ -2433,8 +2426,8 @@ PQgetCopyData(PGconn *conn, char **buffer, int async)
     if (conn->asyncStatus != PGASYNC_COPY_OUT &&
         conn->asyncStatus != PGASYNC_COPY_BOTH)
     {
-        printfPQExpBuffer(&conn->errorMessage,
-                          libpq_gettext("no COPY in progress\n"));
+        appendPQExpBufferStr(&conn->errorMessage,
+                             libpq_gettext("no COPY in progress\n"));
         return -2;
     }
     if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
@@ -2617,14 +2610,14 @@ PQfn(PGconn *conn,
     if (!conn)
         return NULL;

-    /* clear the error string */
+    /* Clear the error string, like PQsendQueryStart() */
     resetPQExpBuffer(&conn->errorMessage);

     if (conn->sock == PGINVALID_SOCKET || conn->asyncStatus != PGASYNC_IDLE ||
         conn->result != NULL)
     {
-        printfPQExpBuffer(&conn->errorMessage,
-                          libpq_gettext("connection in wrong state\n"));
+        appendPQExpBufferStr(&conn->errorMessage,
+                             libpq_gettext("connection in wrong state\n"));
         return NULL;
     }

@@ -3343,8 +3336,8 @@ PQescapeStringInternal(PGconn *conn,
             if (error)
                 *error = 1;
             if (conn)
-                printfPQExpBuffer(&conn->errorMessage,
-                                  libpq_gettext("incomplete multibyte character\n"));
+                appendPQExpBufferStr(&conn->errorMessage,
+                                     libpq_gettext("incomplete multibyte character\n"));
             for (; i < len; i++)
             {
                 if (((size_t) (target - to)) / 2 >= length)
@@ -3427,8 +3420,8 @@ 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,
-                                  libpq_gettext("incomplete multibyte character\n"));
+                appendPQExpBufferStr(&conn->errorMessage,
+                                     libpq_gettext("incomplete multibyte character\n"));
                 return NULL;
             }

@@ -3445,8 +3438,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,
-                          libpq_gettext("out of memory\n"));
+        pqReportOOM(conn);
         return NULL;
     }

@@ -3610,8 +3602,7 @@ PQescapeByteaInternal(PGconn *conn,
     if (rp == NULL)
     {
         if (conn)
-            printfPQExpBuffer(&conn->errorMessage,
-                              libpq_gettext("out of memory\n"));
+            pqReportOOM(conn);
         return NULL;
     }

diff --git a/src/interfaces/libpq/fe-lobj.c b/src/interfaces/libpq/fe-lobj.c
index a5ad3af..9259551 100644
--- a/src/interfaces/libpq/fe-lobj.c
+++ b/src/interfaces/libpq/fe-lobj.c
@@ -61,7 +61,13 @@ lo_open(PGconn *conn, Oid lobjId, int mode)
     PQArgBlock    argv[2];
     PGresult   *res;

-    if (conn == NULL || conn->lobjfuncs == NULL)
+    if (conn == NULL)
+        return -1;
+
+    /* This is a library entry point, so start with an empty error message. */
+    resetPQExpBuffer(&conn->errorMessage);
+
+    if (conn->lobjfuncs == NULL)
     {
         if (lo_initialize(conn) < 0)
             return -1;
@@ -103,7 +109,13 @@ lo_close(PGconn *conn, int fd)
     int            retval;
     int            result_len;

-    if (conn == NULL || conn->lobjfuncs == NULL)
+    if (conn == NULL)
+        return -1;
+
+    /* This is a library entry point, so start with an empty error message. */
+    resetPQExpBuffer(&conn->errorMessage);
+
+    if (conn->lobjfuncs == NULL)
     {
         if (lo_initialize(conn) < 0)
             return -1;
@@ -141,7 +153,13 @@ lo_truncate(PGconn *conn, int fd, size_t len)
     int            retval;
     int            result_len;

-    if (conn == NULL || conn->lobjfuncs == NULL)
+    if (conn == NULL)
+        return -1;
+
+    /* This is a library entry point, so start with an empty error message. */
+    resetPQExpBuffer(&conn->errorMessage);
+
+    if (conn->lobjfuncs == NULL)
     {
         if (lo_initialize(conn) < 0)
             return -1;
@@ -150,8 +168,8 @@ lo_truncate(PGconn *conn, int fd, size_t len)
     /* Must check this on-the-fly because it's not there pre-8.3 */
     if (conn->lobjfuncs->fn_lo_truncate == 0)
     {
-        printfPQExpBuffer(&conn->errorMessage,
-                          libpq_gettext("cannot determine OID of function lo_truncate\n"));
+        appendPQExpBufferStr(&conn->errorMessage,
+                             libpq_gettext("cannot determine OID of function lo_truncate\n"));
         return -1;
     }

@@ -166,8 +184,8 @@ lo_truncate(PGconn *conn, int fd, size_t len)
      */
     if (len > (size_t) INT_MAX)
     {
-        printfPQExpBuffer(&conn->errorMessage,
-                          libpq_gettext("argument of lo_truncate exceeds integer range\n"));
+        appendPQExpBufferStr(&conn->errorMessage,
+                             libpq_gettext("argument of lo_truncate exceeds integer range\n"));
         return -1;
     }

@@ -209,7 +227,13 @@ lo_truncate64(PGconn *conn, int fd, pg_int64 len)
     int            retval;
     int            result_len;

-    if (conn == NULL || conn->lobjfuncs == NULL)
+    if (conn == NULL)
+        return -1;
+
+    /* This is a library entry point, so start with an empty error message. */
+    resetPQExpBuffer(&conn->errorMessage);
+
+    if (conn->lobjfuncs == NULL)
     {
         if (lo_initialize(conn) < 0)
             return -1;
@@ -217,8 +241,8 @@ lo_truncate64(PGconn *conn, int fd, pg_int64 len)

     if (conn->lobjfuncs->fn_lo_truncate64 == 0)
     {
-        printfPQExpBuffer(&conn->errorMessage,
-                          libpq_gettext("cannot determine OID of function lo_truncate64\n"));
+        appendPQExpBufferStr(&conn->errorMessage,
+                             libpq_gettext("cannot determine OID of function lo_truncate64\n"));
         return -1;
     }

@@ -261,7 +285,13 @@ lo_read(PGconn *conn, int fd, char *buf, size_t len)
     PGresult   *res;
     int            result_len;

-    if (conn == NULL || conn->lobjfuncs == NULL)
+    if (conn == NULL)
+        return -1;
+
+    /* This is a library entry point, so start with an empty error message. */
+    resetPQExpBuffer(&conn->errorMessage);
+
+    if (conn->lobjfuncs == NULL)
     {
         if (lo_initialize(conn) < 0)
             return -1;
@@ -275,8 +305,8 @@ lo_read(PGconn *conn, int fd, char *buf, size_t len)
      */
     if (len > (size_t) INT_MAX)
     {
-        printfPQExpBuffer(&conn->errorMessage,
-                          libpq_gettext("argument of lo_read exceeds integer range\n"));
+        appendPQExpBufferStr(&conn->errorMessage,
+                             libpq_gettext("argument of lo_read exceeds integer range\n"));
         return -1;
     }

@@ -316,7 +346,13 @@ lo_write(PGconn *conn, int fd, const char *buf, size_t len)
     int            result_len;
     int            retval;

-    if (conn == NULL || conn->lobjfuncs == NULL)
+    if (conn == NULL)
+        return -1;
+
+    /* This is a library entry point, so start with an empty error message. */
+    resetPQExpBuffer(&conn->errorMessage);
+
+    if (conn->lobjfuncs == NULL)
     {
         if (lo_initialize(conn) < 0)
             return -1;
@@ -330,8 +366,8 @@ lo_write(PGconn *conn, int fd, const char *buf, size_t len)
      */
     if (len > (size_t) INT_MAX)
     {
-        printfPQExpBuffer(&conn->errorMessage,
-                          libpq_gettext("argument of lo_write exceeds integer range\n"));
+        appendPQExpBufferStr(&conn->errorMessage,
+                             libpq_gettext("argument of lo_write exceeds integer range\n"));
         return -1;
     }

@@ -369,7 +405,13 @@ lo_lseek(PGconn *conn, int fd, int offset, int whence)
     int            retval;
     int            result_len;

-    if (conn == NULL || conn->lobjfuncs == NULL)
+    if (conn == NULL)
+        return -1;
+
+    /* This is a library entry point, so start with an empty error message. */
+    resetPQExpBuffer(&conn->errorMessage);
+
+    if (conn->lobjfuncs == NULL)
     {
         if (lo_initialize(conn) < 0)
             return -1;
@@ -413,7 +455,13 @@ lo_lseek64(PGconn *conn, int fd, pg_int64 offset, int whence)
     pg_int64    retval;
     int            result_len;

-    if (conn == NULL || conn->lobjfuncs == NULL)
+    if (conn == NULL)
+        return -1;
+
+    /* This is a library entry point, so start with an empty error message. */
+    resetPQExpBuffer(&conn->errorMessage);
+
+    if (conn->lobjfuncs == NULL)
     {
         if (lo_initialize(conn) < 0)
             return -1;
@@ -421,8 +469,8 @@ lo_lseek64(PGconn *conn, int fd, pg_int64 offset, int whence)

     if (conn->lobjfuncs->fn_lo_lseek64 == 0)
     {
-        printfPQExpBuffer(&conn->errorMessage,
-                          libpq_gettext("cannot determine OID of function lo_lseek64\n"));
+        appendPQExpBufferStr(&conn->errorMessage,
+                             libpq_gettext("cannot determine OID of function lo_lseek64\n"));
         return -1;
     }

@@ -469,7 +517,13 @@ lo_creat(PGconn *conn, int mode)
     int            retval;
     int            result_len;

-    if (conn == NULL || conn->lobjfuncs == NULL)
+    if (conn == NULL)
+        return InvalidOid;
+
+    /* This is a library entry point, so start with an empty error message. */
+    resetPQExpBuffer(&conn->errorMessage);
+
+    if (conn->lobjfuncs == NULL)
     {
         if (lo_initialize(conn) < 0)
             return InvalidOid;
@@ -508,7 +562,13 @@ lo_create(PGconn *conn, Oid lobjId)
     int            retval;
     int            result_len;

-    if (conn == NULL || conn->lobjfuncs == NULL)
+    if (conn == NULL)
+        return InvalidOid;
+
+    /* This is a library entry point, so start with an empty error message. */
+    resetPQExpBuffer(&conn->errorMessage);
+
+    if (conn->lobjfuncs == NULL)
     {
         if (lo_initialize(conn) < 0)
             return InvalidOid;
@@ -517,8 +577,8 @@ lo_create(PGconn *conn, Oid lobjId)
     /* Must check this on-the-fly because it's not there pre-8.1 */
     if (conn->lobjfuncs->fn_lo_create == 0)
     {
-        printfPQExpBuffer(&conn->errorMessage,
-                          libpq_gettext("cannot determine OID of function lo_create\n"));
+        appendPQExpBufferStr(&conn->errorMessage,
+                             libpq_gettext("cannot determine OID of function lo_create\n"));
         return InvalidOid;
     }

@@ -552,7 +612,13 @@ lo_tell(PGconn *conn, int fd)
     PGresult   *res;
     int            result_len;

-    if (conn == NULL || conn->lobjfuncs == NULL)
+    if (conn == NULL)
+        return -1;
+
+    /* This is a library entry point, so start with an empty error message. */
+    resetPQExpBuffer(&conn->errorMessage);
+
+    if (conn->lobjfuncs == NULL)
     {
         if (lo_initialize(conn) < 0)
             return -1;
@@ -588,7 +654,13 @@ lo_tell64(PGconn *conn, int fd)
     PGresult   *res;
     int            result_len;

-    if (conn == NULL || conn->lobjfuncs == NULL)
+    if (conn == NULL)
+        return -1;
+
+    /* This is a library entry point, so start with an empty error message. */
+    resetPQExpBuffer(&conn->errorMessage);
+
+    if (conn->lobjfuncs == NULL)
     {
         if (lo_initialize(conn) < 0)
             return -1;
@@ -596,8 +668,8 @@ lo_tell64(PGconn *conn, int fd)

     if (conn->lobjfuncs->fn_lo_tell64 == 0)
     {
-        printfPQExpBuffer(&conn->errorMessage,
-                          libpq_gettext("cannot determine OID of function lo_tell64\n"));
+        appendPQExpBufferStr(&conn->errorMessage,
+                             libpq_gettext("cannot determine OID of function lo_tell64\n"));
         return -1;
     }

@@ -632,7 +704,13 @@ lo_unlink(PGconn *conn, Oid lobjId)
     int            result_len;
     int            retval;

-    if (conn == NULL || conn->lobjfuncs == NULL)
+    if (conn == NULL)
+        return -1;
+
+    /* This is a library entry point, so start with an empty error message. */
+    resetPQExpBuffer(&conn->errorMessage);
+
+    if (conn->lobjfuncs == NULL)
     {
         if (lo_initialize(conn) < 0)
             return -1;
@@ -702,7 +780,7 @@ lo_import_internal(PGconn *conn, const char *filename, Oid oid)
     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, pqStrerror(errno, sebuf, sizeof(sebuf)));
         return InvalidOid;
@@ -757,7 +835,9 @@ lo_import_internal(PGconn *conn, const char *filename, Oid oid)

         (void) lo_close(conn, lobj);
         (void) close(fd);
-        printfPQExpBuffer(&conn->errorMessage,
+        /* Lose any error message reported by lo_close */
+        resetPQExpBuffer(&conn->errorMessage);
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("could not read from file \"%s\": %s\n"),
                           filename,
                           pqStrerror(save_errno, sebuf, sizeof(sebuf)));
@@ -811,7 +891,9 @@ lo_export(PGconn *conn, Oid lobjId, const char *filename)
         int            save_errno = errno;

         (void) lo_close(conn, lobj);
-        printfPQExpBuffer(&conn->errorMessage,
+        /* Lose any error message reported by lo_close */
+        resetPQExpBuffer(&conn->errorMessage);
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("could not open file \"%s\": %s\n"),
                           filename,
                           pqStrerror(save_errno, sebuf, sizeof(sebuf)));
@@ -831,7 +913,9 @@ lo_export(PGconn *conn, Oid lobjId, const char *filename)

             (void) lo_close(conn, lobj);
             (void) close(fd);
-            printfPQExpBuffer(&conn->errorMessage,
+            /* Lose any error message reported by lo_close */
+            resetPQExpBuffer(&conn->errorMessage);
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("could not write to file \"%s\": %s\n"),
                               filename,
                               pqStrerror(save_errno, sebuf, sizeof(sebuf)));
@@ -852,10 +936,10 @@ lo_export(PGconn *conn, Oid lobjId, const char *filename)
         result = -1;
     }

-    /* if we already failed, don't overwrite that msg with a close error */
+    /* if we already failed, we don't need an additional error from close() */
     if (close(fd) && result >= 0)
     {
-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("could not write to file \"%s\": %s\n"),
                           filename, pqStrerror(errno, sebuf, sizeof(sebuf)));
         result = -1;
@@ -882,17 +966,13 @@ lo_initialize(PGconn *conn)
     const char *fname;
     Oid            foid;

-    if (!conn)
-        return -1;
-
     /*
      * Allocate the structure to hold the functions OID's
      */
     lobjfuncs = (PGlobjfuncs *) malloc(sizeof(PGlobjfuncs));
     if (lobjfuncs == NULL)
     {
-        printfPQExpBuffer(&conn->errorMessage,
-                          libpq_gettext("out of memory\n"));
+        pqReportOOM(conn);
         return -1;
     }
     MemSet((char *) lobjfuncs, 0, sizeof(PGlobjfuncs));
@@ -931,6 +1011,7 @@ lo_initialize(PGconn *conn)
             "or proname = 'loread' "
             "or proname = 'lowrite'";

+    /* PQexec will reset errorMessage, but that's OK */
     res = PQexec(conn, query);
     if (res == NULL)
     {
@@ -942,8 +1023,8 @@ lo_initialize(PGconn *conn)
     {
         free(lobjfuncs);
         PQclear(res);
-        printfPQExpBuffer(&conn->errorMessage,
-                          libpq_gettext("query to initialize large object functions did not return data\n"));
+        appendPQExpBufferStr(&conn->errorMessage,
+                             libpq_gettext("query to initialize large object functions did not return data\n"));
         return -1;
     }

@@ -991,57 +1072,57 @@ lo_initialize(PGconn *conn)
      */
     if (lobjfuncs->fn_lo_open == 0)
     {
-        printfPQExpBuffer(&conn->errorMessage,
-                          libpq_gettext("cannot determine OID of function lo_open\n"));
+        appendPQExpBufferStr(&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,
-                          libpq_gettext("cannot determine OID of function lo_close\n"));
+        appendPQExpBufferStr(&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,
-                          libpq_gettext("cannot determine OID of function lo_creat\n"));
+        appendPQExpBufferStr(&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,
-                          libpq_gettext("cannot determine OID of function lo_unlink\n"));
+        appendPQExpBufferStr(&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,
-                          libpq_gettext("cannot determine OID of function lo_lseek\n"));
+        appendPQExpBufferStr(&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,
-                          libpq_gettext("cannot determine OID of function lo_tell\n"));
+        appendPQExpBufferStr(&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,
-                          libpq_gettext("cannot determine OID of function loread\n"));
+        appendPQExpBufferStr(&conn->errorMessage,
+                             libpq_gettext("cannot determine OID of function loread\n"));
         free(lobjfuncs);
         return -1;
     }
     if (lobjfuncs->fn_lo_write == 0)
     {
-        printfPQExpBuffer(&conn->errorMessage,
-                          libpq_gettext("cannot determine OID of function lowrite\n"));
+        appendPQExpBufferStr(&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 2a6637f..81bbb8c 100644
--- a/src/interfaces/libpq/fe-misc.c
+++ b/src/interfaces/libpq/fe-misc.c
@@ -393,8 +393,7 @@ pqCheckOutBufferSpace(size_t bytes_needed, PGconn *conn)
     }

     /* realloc failed. Probably out of memory */
-    printfPQExpBuffer(&conn->errorMessage,
-                      "cannot allocate memory for output buffer\n");
+    pqReportOOM(conn);
     return EOF;
 }

@@ -487,8 +486,7 @@ pqCheckInBufferSpace(size_t bytes_needed, PGconn *conn)
     }

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

@@ -633,8 +631,8 @@ pqReadData(PGconn *conn)

     if (conn->sock == PGINVALID_SOCKET)
     {
-        printfPQExpBuffer(&conn->errorMessage,
-                          libpq_gettext("connection not open\n"));
+        appendPQExpBufferStr(&conn->errorMessage,
+                             libpq_gettext("connection not open\n"));
         return -1;
     }

@@ -802,11 +800,10 @@ retry4:
      * means the connection has been closed.  Cope.
      */
 definitelyEOF:
-    printfPQExpBuffer(&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"));
+    appendPQExpBufferStr(&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"));

     /* Come here if lower-level code already set a suitable errorMessage */
 definitelyFailed:
@@ -834,8 +831,8 @@ pqSendSome(PGconn *conn, int len)

     if (conn->sock == PGINVALID_SOCKET)
     {
-        printfPQExpBuffer(&conn->errorMessage,
-                          libpq_gettext("connection not open\n"));
+        appendPQExpBufferStr(&conn->errorMessage,
+                             libpq_gettext("connection not open\n"));
         /* Discard queued data; no chance it'll ever be sent */
         conn->outCount = 0;
         return -1;
@@ -1005,8 +1002,8 @@ pqWaitTimed(int forRead, int forWrite, PGconn *conn, time_t finish_time)

     if (result == 0)
     {
-        printfPQExpBuffer(&conn->errorMessage,
-                          libpq_gettext("timeout expired\n"));
+        appendPQExpBufferStr(&conn->errorMessage,
+                             libpq_gettext("timeout expired\n"));
         return 1;
     }

@@ -1050,8 +1047,8 @@ pqSocketCheck(PGconn *conn, int forRead, int forWrite, time_t end_time)
         return -1;
     if (conn->sock == PGINVALID_SOCKET)
     {
-        printfPQExpBuffer(&conn->errorMessage,
-                          libpq_gettext("invalid socket\n"));
+        appendPQExpBufferStr(&conn->errorMessage,
+                             libpq_gettext("invalid socket\n"));
         return -1;
     }

@@ -1073,7 +1070,7 @@ pqSocketCheck(PGconn *conn, int forRead, int forWrite, time_t end_time)
     {
         char        sebuf[256];

-        printfPQExpBuffer(&conn->errorMessage,
+        appendPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("select() failed: %s\n"),
                           SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
     }
@@ -1211,11 +1208,49 @@ PQenv2encoding(void)
     return encoding;
 }

+/*
+ * Report an out-of-memory error by appending a message to conn->errorMessage.
+ *
+ * This task is just common enough, and nontrivial enough, to deserve its own
+ * subroutine.
+ */
+void
+pqReportOOM(PGconn *conn)
+{
+    pqReportOOMBuffer(&conn->errorMessage);
+}
+
+/*
+ * As above, but work with a bare error-message-buffer pointer.
+ */
+void
+pqReportOOMBuffer(PQExpBuffer errorMessage)
+{
+    const char *msg = libpq_gettext("out of memory\n");
+
+    /*
+     * First just try to append the message.  Even though we're up against
+     * OOM, this is likely to succeed because of unused space within
+     * errorMessage's buffer.
+     */
+    appendPQExpBufferStr(errorMessage, msg);
+
+    /*
+     * If that didn't succeed, it'll have marked the buffer broken.  Our
+     * fallback plan is to reset the buffer (hopefully regaining some space)
+     * and try again.  If still no luck, not much we can do.
+     */
+    if (PQExpBufferBroken(errorMessage))
+    {
+        resetPQExpBuffer(errorMessage);
+        appendPQExpBufferStr(errorMessage, msg);
+    }
+}

 #ifdef ENABLE_NLS

 static void
-libpq_binddomain()
+libpq_binddomain(void)
 {
     static bool already_bound = false;

diff --git a/src/interfaces/libpq/fe-protocol2.c b/src/interfaces/libpq/fe-protocol2.c
index 53e5083..04a2e47 100644
--- a/src/interfaces/libpq/fe-protocol2.c
+++ b/src/interfaces/libpq/fe-protocol2.c
@@ -85,11 +85,9 @@ pqSetenvPoll(PGconn *conn)
             return PGRES_POLLING_OK;

         default:
-            printfPQExpBuffer(&conn->errorMessage,
-                              libpq_gettext(
-                                            "invalid setenv state %c, "
-                                            "probably indicative of memory corruption\n"
-                                            ),
+            appendPQExpBuffer(&conn->errorMessage,
+                              libpq_gettext("invalid setenv state %c, "
+                                            "probably indicative of memory corruption\n"),
                               conn->setenv_state);
             goto error_return;
     }
@@ -385,7 +383,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);
@@ -498,8 +496,7 @@ pqParseInput2(PGconn *conn)
                                                            PGRES_COMMAND_OK);
                         if (!conn->result)
                         {
-                            printfPQExpBuffer(&conn->errorMessage,
-                                              libpq_gettext("out of memory"));
+                            pqReportOOM(conn);
                             pqSaveErrorResult(conn);
                         }
                     }
@@ -533,8 +530,7 @@ pqParseInput2(PGconn *conn)
                                                            PGRES_EMPTY_QUERY);
                         if (!conn->result)
                         {
-                            printfPQExpBuffer(&conn->errorMessage,
-                                              libpq_gettext("out of memory"));
+                            pqReportOOM(conn);
                             pqSaveErrorResult(conn);
                         }
                     }
@@ -627,9 +623,8 @@ pqParseInput2(PGconn *conn)
                      * never arrives from the server during protocol 2.0.
                      */
                 default:
-                    printfPQExpBuffer(&conn->errorMessage,
-                                      libpq_gettext(
-                                                    "unexpected response from server; first received character was
\"%c\"\n"),
+                    appendPQExpBuffer(&conn->errorMessage,
+                                      libpq_gettext("unexpected response from server; first received character was
\"%c\"\n"),
                                       id);
                     /* build an error result holding the error message */
                     pqSaveErrorResult(conn);
@@ -760,7 +755,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
@@ -935,7 +930,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
@@ -1048,12 +1043,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,
-                              libpq_gettext("out of memory"));
+            pqReportOOM(conn);
         if (conn->xactStatus == PQTRANS_INTRANS)
             conn->xactStatus = PQTRANS_INERROR;
     }
@@ -1209,8 +1202,7 @@ pqGetCopyData2(PGconn *conn, char **buffer, int async)
         *buffer = (char *) malloc(msgLength + 1);
         if (*buffer == NULL)
         {
-            printfPQExpBuffer(&conn->errorMessage,
-                              libpq_gettext("out of memory\n"));
+            pqReportOOM(conn);
             return -2;
         }
         memcpy(*buffer, &conn->inBuffer[conn->inStart], msgLength);
@@ -1355,8 +1347,8 @@ pqEndcopy2(PGconn *conn)
     if (conn->asyncStatus != PGASYNC_COPY_IN &&
         conn->asyncStatus != PGASYNC_COPY_OUT)
     {
-        printfPQExpBuffer(&conn->errorMessage,
-                          libpq_gettext("no COPY in progress\n"));
+        appendPQExpBufferStr(&conn->errorMessage,
+                             libpq_gettext("no COPY in progress\n"));
         return 1;
     }

@@ -1373,7 +1365,6 @@ pqEndcopy2(PGconn *conn)

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

     /* Wait for the completion response */
     result = PQgetResult(conn);
@@ -1544,7 +1535,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);
@@ -1576,7 +1567,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 8345faf..98261d4 100644
--- a/src/interfaces/libpq/fe-protocol3.c
+++ b/src/interfaces/libpq/fe-protocol3.c
@@ -204,8 +204,7 @@ pqParseInput3(PGconn *conn)
                                                            PGRES_COMMAND_OK);
                         if (!conn->result)
                         {
-                            printfPQExpBuffer(&conn->errorMessage,
-                                              libpq_gettext("out of memory"));
+                            pqReportOOM(conn);
                             pqSaveErrorResult(conn);
                         }
                     }
@@ -231,8 +230,7 @@ pqParseInput3(PGconn *conn)
                                                            PGRES_EMPTY_QUERY);
                         if (!conn->result)
                         {
-                            printfPQExpBuffer(&conn->errorMessage,
-                                              libpq_gettext("out of memory"));
+                            pqReportOOM(conn);
                             pqSaveErrorResult(conn);
                         }
                     }
@@ -248,8 +246,7 @@ pqParseInput3(PGconn *conn)
                                                                PGRES_COMMAND_OK);
                             if (!conn->result)
                             {
-                                printfPQExpBuffer(&conn->errorMessage,
-                                                  libpq_gettext("out of memory"));
+                                pqReportOOM(conn);
                                 pqSaveErrorResult(conn);
                             }
                         }
@@ -328,8 +325,7 @@ pqParseInput3(PGconn *conn)
                                                                PGRES_COMMAND_OK);
                             if (!conn->result)
                             {
-                                printfPQExpBuffer(&conn->errorMessage,
-                                                  libpq_gettext("out of memory"));
+                                pqReportOOM(conn);
                                 pqSaveErrorResult(conn);
                             }
                         }
@@ -363,8 +359,8 @@ pqParseInput3(PGconn *conn)
                     else
                     {
                         /* Set up to report error at end of query */
-                        printfPQExpBuffer(&conn->errorMessage,
-                                          libpq_gettext("server sent data (\"D\" message) without prior row
description(\"T\" message)\n")); 
+                        appendPQExpBufferStr(&conn->errorMessage,
+                                             libpq_gettext("server sent data (\"D\" message) without prior row
description(\"T\" message)\n")); 
                         pqSaveErrorResult(conn);
                         /* Discard the unexpected message */
                         conn->inCursor += msgLength;
@@ -406,9 +402,8 @@ pqParseInput3(PGconn *conn)
                      */
                     break;
                 default:
-                    printfPQExpBuffer(&conn->errorMessage,
-                                      libpq_gettext(
-                                                    "unexpected response from server; first received character was
\"%c\"\n"),
+                    appendPQExpBuffer(&conn->errorMessage,
+                                      libpq_gettext("unexpected response from server; first received character was
\"%c\"\n"),
                                       id);
                     /* build an error result holding the error message */
                     pqSaveErrorResult(conn);
@@ -428,7 +423,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 */
@@ -448,9 +443,8 @@ pqParseInput3(PGconn *conn)
 static void
 handleSyncLoss(PGconn *conn, char id, int msgLength)
 {
-    printfPQExpBuffer(&conn->errorMessage,
-                      libpq_gettext(
-                                    "lost synchronization with server: got message type \"%c\", length %d\n"),
+    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 */
     pqSaveErrorResult(conn);
@@ -625,7 +619,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);

     /*
@@ -725,7 +719,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);

     /*
@@ -852,7 +846,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);

     /*
@@ -953,9 +947,10 @@ pqGetErrorNotice3(PGconn *conn, bool isError)
             res->errMsg = pqResultStrdup(res, workBuf.data);
         pqClearAsyncResult(conn);    /* redundant, but be safe */
         conn->result = res;
+
+        /* We also keep a running history of errors in conn->errorMessage. */
         if (PQExpBufferDataBroken(workBuf))
-            printfPQExpBuffer(&conn->errorMessage,
-                              libpq_gettext("out of memory"));
+            pqReportOOM(conn);
         else
             appendPQExpBufferStr(&conn->errorMessage, workBuf.data);
     }
@@ -1681,8 +1676,7 @@ pqGetCopyData3(PGconn *conn, char **buffer, int async)
             *buffer = (char *) malloc(msgLength + 1);
             if (*buffer == NULL)
             {
-                printfPQExpBuffer(&conn->errorMessage,
-                                  libpq_gettext("out of memory\n"));
+                pqReportOOM(conn);
                 return -2;
             }
             memcpy(*buffer, &conn->inBuffer[conn->inCursor], msgLength);
@@ -1714,8 +1708,8 @@ pqGetline3(PGconn *conn, char *s, int maxlen)
          conn->asyncStatus != PGASYNC_COPY_BOTH) ||
         conn->copy_is_binary)
     {
-        printfPQExpBuffer(&conn->errorMessage,
-                          libpq_gettext("PQgetline: not doing text COPY OUT\n"));
+        appendPQExpBufferStr(&conn->errorMessage,
+                             libpq_gettext("PQgetline: not doing text COPY OUT\n"));
         *s = '\0';
         return EOF;
     }
@@ -1820,8 +1814,8 @@ pqEndcopy3(PGconn *conn)
         conn->asyncStatus != PGASYNC_COPY_OUT &&
         conn->asyncStatus != PGASYNC_COPY_BOTH)
     {
-        printfPQExpBuffer(&conn->errorMessage,
-                          libpq_gettext("no COPY in progress\n"));
+        appendPQExpBufferStr(&conn->errorMessage,
+                             libpq_gettext("no COPY in progress\n"));
         return 1;
     }

@@ -1854,7 +1848,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
@@ -2092,7 +2085,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 b3f580f..2bea0d2 100644
--- a/src/interfaces/libpq/fe-secure-common.c
+++ b/src/interfaces/libpq/fe-secure-common.c
@@ -94,8 +94,8 @@ pq_verify_peer_name_matches_certificate_name(PGconn *conn,

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

@@ -106,8 +106,7 @@ pq_verify_peer_name_matches_certificate_name(PGconn *conn,
     name = malloc(namelen + 1);
     if (name == NULL)
     {
-        printfPQExpBuffer(&conn->errorMessage,
-                          libpq_gettext("out of memory\n"));
+        pqReportOOM(conn);
         return -1;
     }
     memcpy(name, namedata, namelen);
@@ -120,8 +119,8 @@ pq_verify_peer_name_matches_certificate_name(PGconn *conn,
     if (namelen != strlen(name))
     {
         free(name);
-        printfPQExpBuffer(&conn->errorMessage,
-                          libpq_gettext("SSL certificate's name contains embedded null\n"));
+        appendPQExpBufferStr(&conn->errorMessage,
+                             libpq_gettext("SSL certificate's name contains embedded null\n"));
         return -1;
     }

@@ -167,8 +166,8 @@ 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,
-                          libpq_gettext("host name must be specified for a verified SSL connection\n"));
+        appendPQExpBufferStr(&conn->errorMessage,
+                             libpq_gettext("host name must be specified for a verified SSL connection\n"));
         return false;
     }

@@ -184,7 +183,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,14 +191,14 @@ 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,
-                              libpq_gettext("could not get server's host name from server certificate\n"));
+            appendPQExpBufferStr(&conn->errorMessage,
+                                 libpq_gettext("could not get server's host name from server certificate\n"));
         }
     }

diff --git a/src/interfaces/libpq/fe-secure-openssl.c b/src/interfaces/libpq/fe-secure-openssl.c
index bbae8ef..2cf3e22 100644
--- a/src/interfaces/libpq/fe-secure-openssl.c
+++ b/src/interfaces/libpq/fe-secure-openssl.c
@@ -177,8 +177,8 @@ rloop:
             if (n < 0)
             {
                 /* Not supposed to happen, so we don't translate the msg */
-                printfPQExpBuffer(&conn->errorMessage,
-                                  "SSL_read failed but did not provide error information\n");
+                appendPQExpBufferStr(&conn->errorMessage,
+                                     "SSL_read failed but did not provide error information\n");
                 /* assume the connection is broken */
                 result_errno = ECONNRESET;
             }
@@ -201,21 +201,20 @@ rloop:
                 result_errno = SOCK_ERRNO;
                 if (result_errno == EPIPE ||
                     result_errno == ECONNRESET)
-                    printfPQExpBuffer(&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"));
+                    appendPQExpBufferStr(&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,
-                                  libpq_gettext("SSL SYSCALL error: EOF detected\n"));
+                appendPQExpBufferStr(&conn->errorMessage,
+                                     libpq_gettext("SSL SYSCALL error: EOF detected\n"));
                 /* assume the connection is broken */
                 result_errno = ECONNRESET;
                 n = -1;
@@ -225,7 +224,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 */
@@ -240,13 +239,13 @@ rloop:
              * a clean connection closure, so we should not report it as a
              * server crash.
              */
-            printfPQExpBuffer(&conn->errorMessage,
-                              libpq_gettext("SSL connection has been closed unexpectedly\n"));
+            appendPQExpBufferStr(&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 */
@@ -287,8 +286,8 @@ 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,
-                                  "SSL_write failed but did not provide error information\n");
+                appendPQExpBufferStr(&conn->errorMessage,
+                                     "SSL_write failed but did not provide error information\n");
                 /* assume the connection is broken */
                 result_errno = ECONNRESET;
             }
@@ -309,21 +308,20 @@ pgtls_write(PGconn *conn, const void *ptr, size_t len)
             {
                 result_errno = SOCK_ERRNO;
                 if (result_errno == EPIPE || result_errno == ECONNRESET)
-                    printfPQExpBuffer(&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"));
+                    appendPQExpBufferStr(&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,
-                                  libpq_gettext("SSL SYSCALL error: EOF detected\n"));
+                appendPQExpBufferStr(&conn->errorMessage,
+                                     libpq_gettext("SSL SYSCALL error: EOF detected\n"));
                 /* assume the connection is broken */
                 result_errno = ECONNRESET;
                 n = -1;
@@ -333,7 +331,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 */
@@ -348,13 +346,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,
-                              libpq_gettext("SSL connection has been closed unexpectedly\n"));
+            appendPQExpBufferStr(&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 */
@@ -394,8 +392,8 @@ 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,
-                          libpq_gettext("could not determine server certificate signature algorithm\n"));
+        appendPQExpBufferStr(&conn->errorMessage,
+                             libpq_gettext("could not determine server certificate signature algorithm\n"));
         return NULL;
     }

@@ -415,7 +413,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;
@@ -425,8 +423,8 @@ pgtls_get_peer_certificate_hash(PGconn *conn, size_t *len)

     if (!X509_digest(peer_cert, algo_type, hash, &hash_size))
     {
-        printfPQExpBuffer(&conn->errorMessage,
-                          libpq_gettext("could not generate peer certificate hash\n"));
+        appendPQExpBufferStr(&conn->errorMessage,
+                             libpq_gettext("could not generate peer certificate hash\n"));
         return NULL;
     }

@@ -434,8 +432,7 @@ pgtls_get_peer_certificate_hash(PGconn *conn, size_t *len)
     cert_hash = malloc(hash_size);
     if (cert_hash == NULL)
     {
-        printfPQExpBuffer(&conn->errorMessage,
-                          libpq_gettext("out of memory\n"));
+        pqReportOOM(conn);
         return NULL;
     }
     memcpy(cert_hash, hash, hash_size);
@@ -443,7 +440,7 @@ pgtls_get_peer_certificate_hash(PGconn *conn, size_t *len)

     return cert_hash;
 }
-#endif /* HAVE_X509_GET_SIGNATURE_NID */
+#endif                            /* HAVE_X509_GET_SIGNATURE_NID */

 /* ------------------------------------------------------------ */
 /*                        OpenSSL specific code                    */
@@ -482,8 +479,8 @@ openssl_verify_peer_name_matches_certificate_name(PGconn *conn, ASN1_STRING *nam
     /* Should not happen... */
     if (name_entry == NULL)
     {
-        printfPQExpBuffer(&conn->errorMessage,
-                          libpq_gettext("SSL certificate's name entry is missing\n"));
+        appendPQExpBufferStr(&conn->errorMessage,
+                             libpq_gettext("SSL certificate's name entry is missing\n"));
         return -1;
     }

@@ -811,7 +808,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);
@@ -848,7 +845,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);
@@ -876,7 +873,7 @@ initialize_SSL(PGconn *conn)
 #else
                 char       *err = SSLerrmessage(ERR_get_error());

-                printfPQExpBuffer(&conn->errorMessage,
+                appendPQExpBuffer(&conn->errorMessage,
                                   libpq_gettext("SSL library does not support CRL certificates (file \"%s\")\n"),
                                   fnbuf);
                 SSLerrfree(err);
@@ -904,11 +901,11 @@ initialize_SSL(PGconn *conn)
              * that it seems worth having a specialized error message for it.
              */
             if (fnbuf[0] == '\0')
-                printfPQExpBuffer(&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")); 
+                appendPQExpBufferStr(&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);
@@ -939,7 +936,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, pqStrerror(errno, sebuf, sizeof(sebuf)));
             SSL_CTX_free(SSL_context);
@@ -958,7 +955,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);
@@ -983,7 +980,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);
@@ -1021,8 +1018,7 @@ initialize_SSL(PGconn *conn)

             if (engine_str == NULL)
             {
-                printfPQExpBuffer(&conn->errorMessage,
-                                  libpq_gettext("out of memory\n"));
+                pqReportOOM(conn);
                 return -1;
             }

@@ -1037,7 +1033,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);
@@ -1049,7 +1045,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);
@@ -1065,7 +1061,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);
@@ -1079,7 +1075,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);
@@ -1116,7 +1112,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;
@@ -1124,7 +1120,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;
@@ -1135,7 +1131,7 @@ initialize_SSL(PGconn *conn)
         {
             char       *err = SSLerrmessage(ERR_get_error());

-            printfPQExpBuffer(&conn->errorMessage,
+            appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("could not load private key file \"%s\": %s\n"),
                               fnbuf, err);
             SSLerrfree(err);
@@ -1149,7 +1145,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);
@@ -1215,12 +1211,12 @@ open_client_SSL(PGconn *conn)
                     char        sebuf[256];

                     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,
-                                          libpq_gettext("SSL SYSCALL error: EOF detected\n"));
+                        appendPQExpBufferStr(&conn->errorMessage,
+                                             libpq_gettext("SSL SYSCALL error: EOF detected\n"));
                     pgtls_close(conn);
                     return PGRES_POLLING_FAILED;
                 }
@@ -1228,7 +1224,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);
@@ -1237,7 +1233,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);
@@ -1258,7 +1254,7 @@ open_client_SSL(PGconn *conn)

         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 f7dc249..157a738 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
@@ -256,16 +256,15 @@ pqsecure_raw_read(PGconn *conn, void *ptr, size_t len)

 #ifdef ECONNRESET
             case ECONNRESET:
-                printfPQExpBuffer(&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"));
+                appendPQExpBufferStr(&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;
 #endif

             default:
-                printfPQExpBuffer(&conn->errorMessage,
+                appendPQExpBuffer(&conn->errorMessage,
                                   libpq_gettext("could not receive data from server: %s\n"),
                                   SOCK_STRERROR(result_errno,
                                                 sebuf, sizeof(sebuf)));
@@ -282,8 +281,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
@@ -366,15 +365,14 @@ retry_masked:

             case ECONNRESET:
 #endif
-                printfPQExpBuffer(&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"));
+                appendPQExpBufferStr(&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 bc60373..22cfa4f 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -644,6 +644,8 @@ extern int pqWaitTimed(int forRead, int forWrite, PGconn *conn,
             time_t finish_time);
 extern int    pqReadReady(PGconn *conn);
 extern int    pqWriteReady(PGconn *conn);
+extern void pqReportOOM(PGconn *conn);
+extern void pqReportOOMBuffer(PQExpBuffer errorMessage);

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

@@ -679,7 +681,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 (with a message added to conn->errorMessage).
  */
 extern int    pgtls_init(PGconn *conn);

@@ -696,8 +698,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);
@@ -710,8 +712,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/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 647423c..3f278c3 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -2116,6 +2116,31 @@ keep_going:                        /* We will come back to here until there is
             goto error_return;
         }
         conn->whichhost++;
+
+        /*
+         * If we're given more than one target host, append identification
+         * info to conn->errorMessage as we start to consider each one.  This
+         * makes it easier to figure out which host error messages apply to,
+         * in case we fail to connect to all of them.
+         */
+        if (conn->nconnhost > 1)
+        {
+            pg_conn_host *ch = &conn->connhost[conn->whichhost];
+            const char *displayed_host;
+            const char *displayed_port;
+
+            if (ch->type == CHT_HOST_ADDRESS)
+                displayed_host = ch->hostaddr;
+            else
+                displayed_host = ch->host;
+            displayed_port = ch->port;
+            if (displayed_port == NULL || displayed_port[0] == '\0')
+                displayed_port = DEF_PGPORT_STR;
+            appendPQExpBuffer(&conn->errorMessage,
+                              libpq_gettext("server \"%s\" port %s: "),
+                              displayed_host, displayed_port);
+        }
+
         conn->addr_cur = conn->connhost[conn->whichhost].addrlist;
         /* If no addresses for this host, just try the next one */
         if (conn->addr_cur == NULL)
@@ -3154,9 +3179,6 @@ keep_going:                        /* We will come back to here until there is
             }
         case CONNECTION_CHECK_WRITABLE:
             {
-                const char *displayed_host;
-                const char *displayed_port;
-
                 if (!PQconsumeInput(conn))
                     goto error_return;

@@ -3173,25 +3195,10 @@ keep_going:                        /* We will come back to here until there is
                     if (strncmp(val, "on", 2) == 0)
                     {
                         /* Not writable; fail this connection. */
-                        const char *displayed_host;
-                        const char *displayed_port;
-
                         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("could not open a read-write session\n"));

                         /* Close connection politely. */
                         conn->status = CONNECTION_OK;
@@ -3217,25 +3224,17 @@ keep_going:                        /* We will come back to here until there is
                 }

                 /*
-                 * Something went wrong with "SHOW transaction_read_only". We
-                 * should try next addresses.
+                 * Something went wrong with "SHOW transaction_read_only".  If
+                 * there was a server error, it'll already have been appended
+                 * to errorMessage, else say something generic.
                  */
+                if (res == NULL || PQresultStatus(res) != PGRES_FATAL_ERROR)
+                    appendPQExpBufferStr(&conn->errorMessage,
+                                         libpq_gettext("\"SHOW transaction_read_only\" failed\n"));
+
                 if (res)
                     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);
-
                 /* Close connection politely. */
                 conn->status = CONNECTION_OK;
                 sendTerminateConn(conn);

В списке pgsql-hackers по дате отправления:

Предыдущее
От: Andrew Dunstan
Дата:
Сообщение: Re: [HACKERS] pgbench - allow to store select results into variables
Следующее
От: Fabien COELHO
Дата:
Сообщение: Re: [HACKERS] pgbench - allow to store select results intovariables