libpq: Fix another bug in 721f7bd3cbccaf8c07cad2707826b83f84694832.
authorRobert Haas <rhaas@postgresql.org>
Mon, 5 Dec 2016 19:09:54 +0000 (14:09 -0500)
committerRobert Haas <rhaas@postgresql.org>
Mon, 5 Dec 2016 19:11:52 +0000 (14:11 -0500)
If we failed to connect to one or more hosts, and then afterwards we
find one that fails to be read-write, the latter error message was
clobbering any earlier ones.  Repair.

Mithun Cy, slightly revised by me.

src/interfaces/libpq/fe-connect.c

index 3b9b263a394f8d29f6b09aadd5c930847c6bf6a4..101cce8673fc46efbba2c5817f0324cc26d56b54 100644 (file)
@@ -1762,6 +1762,39 @@ connectDBComplete(PGconn *conn)
    }
 }
 
+/*
+ * This subroutine saves conn->errorMessage, which will be restored back by
+ * restoreErrorMessage subroutine.
+ */
+static bool
+saveErrorMessage(PGconn *conn, PQExpBuffer savedMessage)
+{
+   initPQExpBuffer(savedMessage);
+   if (PQExpBufferBroken(savedMessage))
+   {
+       printfPQExpBuffer(&conn->errorMessage,
+                         libpq_gettext("out of memory\n"));
+       return false;
+   }
+
+   appendPQExpBufferStr(savedMessage,
+                        conn->errorMessage.data);
+   resetPQExpBuffer(&conn->errorMessage);
+   return true;
+}
+
+/*
+ * Restores saved error messages back to conn->errorMessage.
+ */
+static void
+restoreErrorMessage(PGconn *conn, PQExpBuffer savedMessage)
+{
+   appendPQExpBufferStr(savedMessage, conn->errorMessage.data);
+   resetPQExpBuffer(&conn->errorMessage);
+   appendPQExpBufferStr(&conn->errorMessage, savedMessage->data);
+   termPQExpBuffer(savedMessage);
+}
+
 /* ----------------
  *     PQconnectPoll
  *
@@ -1795,6 +1828,7 @@ PQconnectPoll(PGconn *conn)
    PGresult   *res;
    char        sebuf[256];
    int         optval;
+   PQExpBufferData savedMessage;
 
    if (conn == NULL)
        return PGRES_POLLING_FAILED;
@@ -2792,11 +2826,26 @@ keep_going:                     /* We will come back to here until there is
                if (conn->target_session_attrs != NULL &&
                    strcmp(conn->target_session_attrs, "read-write") == 0)
                {
+                   /*
+                    * We are yet to make a connection. Save all existing error
+                    * messages until we make a successful connection state.
+                    * This is important because PQsendQuery is going to reset
+                    * conn->errorMessage and we will loose error messages
+                    * related to previous hosts we have tried to connect and
+                    * failed.
+                    */
+                   if (!saveErrorMessage(conn, &savedMessage))
+                       goto error_return;
+
                    conn->status = CONNECTION_OK;
                    if (!PQsendQuery(conn,
                                     "show transaction_read_only"))
+                   {
+                       restoreErrorMessage(conn, &savedMessage);
                        goto error_return;
+                   }
                    conn->status = CONNECTION_CHECK_WRITABLE;
+                   restoreErrorMessage(conn, &savedMessage);
                    return PGRES_POLLING_READING;
                }
 
@@ -2841,11 +2890,18 @@ keep_going:                     /* We will come back to here until there is
            if (conn->target_session_attrs != NULL &&
                strcmp(conn->target_session_attrs, "read-write") == 0)
            {
+               if (!saveErrorMessage(conn, &savedMessage))
+                   goto error_return;
+
                conn->status = CONNECTION_OK;
                if (!PQsendQuery(conn,
                                 "show transaction_read_only"))
+               {
+                   restoreErrorMessage(conn, &savedMessage);
                    goto error_return;
+               }
                conn->status = CONNECTION_CHECK_WRITABLE;
+               restoreErrorMessage(conn, &savedMessage);
                return PGRES_POLLING_READING;
            }
 
@@ -2858,13 +2914,20 @@ keep_going:                     /* We will come back to here until there is
 
        case CONNECTION_CHECK_WRITABLE:
            {
+               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;
                }
 
@@ -2878,6 +2941,7 @@ keep_going:                       /* We will come back to here until there is
                    if (strncmp(val, "on", 2) == 0)
                    {
                        PQclear(res);
+                       restoreErrorMessage(conn, &savedMessage);
 
                        /* Not writable; close connection. */
                        appendPQExpBuffer(&conn->errorMessage,
@@ -2902,6 +2966,7 @@ keep_going:                       /* We will come back to here until there is
                        goto error_return;
                    }
                    PQclear(res);
+                   termPQExpBuffer(&savedMessage);
 
                    /* We can release the address lists now. */
                    release_all_addrinfo(conn);
@@ -2917,6 +2982,7 @@ keep_going:                       /* We will come back to here until there is
                 */
                if (res)
                    PQclear(res);
+               restoreErrorMessage(conn, &savedMessage);
                appendPQExpBuffer(&conn->errorMessage,
                  libpq_gettext("test \"show transaction_read_only\" failed "
                                " on \"%s:%s\" \n"),