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

Поиск
Список
Период
Сортировка
От Tom Lane
Тема Re: Multiple hosts in connection string failed to failover in non-hot standby mode
Дата
Msg-id 770711.1610318330@sss.pgh.pa.us
обсуждение исходный текст
Ответ на Re: Multiple hosts in connection string failed to failover in non-hot standby mode  (Tom Lane <tgl@sss.pgh.pa.us>)
Ответы Re: Multiple hosts in connection string failed to failover in non-hot standby mode  (Tom Lane <tgl@sss.pgh.pa.us>)
Список pgsql-hackers
I wrote:
> The problems that I see in this area are first that there's no
> real standardization in libpq as to whether to append error messages
> together or just flush preceding messages; and second that no effort
> is made in multi-connection-attempt cases to separate the errors from
> different attempts, much less identify which error goes with which
> host or IP address.  I think we really need to put some work into
> that.

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

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

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

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

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

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

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

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

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

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

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

            regards, tom lane

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

             PqGSSRecvLength += ret;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

     /* Buffer for receiving various parts of messages */
@@ -600,7 +604,6 @@ extern pgthreadlock_t pg_g_threadlock;
 /* === in fe-exec.c === */

 extern void pqSetResultError(PGresult *res, const char *msg);
-extern void pqCatenateResultError(PGresult *res, const char *msg);
 extern void *pqResultAlloc(PGresult *res, size_t nBytes, bool isBinary);
 extern char *pqResultStrdup(PGresult *res, const char *str);
 extern void pqClearAsyncResult(PGconn *conn);
@@ -612,6 +615,7 @@ extern void pqSaveMessageField(PGresult *res, char code,
 extern void pqSaveParameterStatus(PGconn *conn, const char *name,
                                   const char *value);
 extern int    pqRowProcessor(PGconn *conn, const char **errmsgp);
+extern int    PQsendQueryContinue(PGconn *conn, const char *query);

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

Предыдущее
От: Tomas Vondra
Дата:
Сообщение: Re: COPY FREEZE and setting PD_ALL_VISIBLE/visibility map bits
Следующее
От: Bruce Momjian
Дата:
Сообщение: Re: Inconsistent "" use