aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/interfaces/libpq/fe-connect.c459
-rw-r--r--src/interfaces/libpq/libpq-int.h38
2 files changed, 297 insertions, 200 deletions
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 221f1ecdaf8..986f74ff006 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -415,7 +415,8 @@ pgthreadlock_t pg_g_threadlock = default_threadlock;
*
* Close any physical connection to the server, and reset associated
* state inside the connection object. We don't release state that
- * would be needed to reconnect, though.
+ * would be needed to reconnect, though, nor local state that might still
+ * be useful later.
*
* We can always flush the output buffer, since there's no longer any hope
* of sending that data. However, unprocessed input data might still be
@@ -483,6 +484,64 @@ pqDropConnection(PGconn *conn, bool flushInput)
/*
+ * pqDropServerData
+ *
+ * Clear all connection state data that was received from (or deduced about)
+ * the server. This is essential to do between connection attempts to
+ * different servers, else we may incorrectly hold over some data from the
+ * old server.
+ *
+ * It would be better to merge this into pqDropConnection, perhaps, but
+ * right now we cannot because that function is called immediately on
+ * detection of connection loss (cf. pqReadData, for instance). This data
+ * should be kept until we are actually starting a new connection.
+ */
+static void
+pqDropServerData(PGconn *conn)
+{
+ PGnotify *notify;
+ pgParameterStatus *pstatus;
+
+ /* Forget pending notifies */
+ notify = conn->notifyHead;
+ while (notify != NULL)
+ {
+ PGnotify *prev = notify;
+
+ notify = notify->next;
+ free(prev);
+ }
+ conn->notifyHead = conn->notifyTail = NULL;
+
+ /* Reset ParameterStatus data, as well as variables deduced from it */
+ pstatus = conn->pstatus;
+ while (pstatus != NULL)
+ {
+ pgParameterStatus *prev = pstatus;
+
+ pstatus = pstatus->next;
+ free(prev);
+ }
+ conn->pstatus = NULL;
+ conn->client_encoding = PG_SQL_ASCII;
+ conn->std_strings = false;
+ conn->sversion = 0;
+
+ /* Drop large-object lookup data */
+ if (conn->lobjfuncs)
+ free(conn->lobjfuncs);
+ conn->lobjfuncs = NULL;
+
+ /* Reset assorted other per-connection state */
+ conn->last_sqlstate[0] = '\0';
+ conn->auth_req_received = false;
+ conn->password_needed = false;
+ conn->be_pid = 0;
+ conn->be_key = 0;
+}
+
+
+/*
* Connecting to a Database
*
* There are now six different ways a user of this API can connect to the
@@ -1110,9 +1169,6 @@ connectOptions2(PGconn *conn)
conn->dbName,
conn->pguser,
conn->pgpassfile);
- /* If we got one, set pgpassfile_used */
- if (conn->connhost[i].password != NULL)
- conn->pgpassfile_used = true;
}
}
}
@@ -1701,6 +1757,14 @@ connectDBStart(PGconn *conn)
conn->outCount = 0;
/*
+ * Ensure errorMessage is empty, too. PQconnectPoll will append messages
+ * to it in the process of scanning for a working server. Thus, if we
+ * fail to connect to multiple hosts, the final error message will include
+ * details about each failure.
+ */
+ resetPQExpBuffer(&conn->errorMessage);
+
+ /*
* Look up socket addresses for each possible host using
* pg_getaddrinfo_all.
*/
@@ -1795,21 +1859,14 @@ connectDBStart(PGconn *conn)
}
}
-#ifdef USE_SSL
- /* setup values based on SSL mode */
- if (conn->sslmode[0] == 'd') /* "disable" */
- conn->allow_ssl_try = false;
- else if (conn->sslmode[0] == 'a') /* "allow" */
- conn->wait_ssl_try = true;
-#endif
-
/*
- * Set up to try to connect, with protocol 3.0 as the first attempt.
+ * 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
+ * anything else looks at it.)
*/
- conn->whichhost = 0;
- conn->addr_cur = conn->connhost[0].addrlist;
- conn->pversion = PG_PROTOCOL(3, 0);
- conn->send_appname = true;
+ conn->whichhost = -1;
+ conn->try_next_addr = false;
+ conn->try_next_host = true;
conn->status = CONNECTION_NEEDED;
/*
@@ -1823,6 +1880,12 @@ connectDBStart(PGconn *conn)
return 1;
connect_errReturn:
+
+ /*
+ * If we managed to open a socket, close it immediately rather than
+ * waiting till PQfinish. (The application cannot have gotten the socket
+ * from PQsocket yet, so this doesn't risk breaking anything.)
+ */
pqDropConnection(conn, true);
conn->status = CONNECTION_BAD;
return 0;
@@ -1887,6 +1950,7 @@ connectDBComplete(PGconn *conn)
ret = pqWaitTimed(1, 0, conn, finish_time);
if (ret == -1)
{
+ /* hard failure, eg select() problem, aborts everything */
conn->status = CONNECTION_BAD;
return 0;
}
@@ -1896,6 +1960,7 @@ connectDBComplete(PGconn *conn)
ret = pqWaitTimed(0, 1, conn, finish_time);
if (ret == -1)
{
+ /* hard failure, eg select() problem, aborts everything */
conn->status = CONNECTION_BAD;
return 0;
}
@@ -1910,24 +1975,17 @@ connectDBComplete(PGconn *conn)
if (ret == 1) /* connect_timeout elapsed */
{
/*
- * If there are no more hosts, return (the error message is
- * already set)
+ * Attempt connection to the next host, ignoring any remaining
+ * addresses for the current host.
*/
- if (++conn->whichhost >= conn->nconnhost)
- {
- conn->whichhost = 0;
- conn->status = CONNECTION_BAD;
- return 0;
- }
+ conn->try_next_addr = false;
+ conn->try_next_host = true;
+ conn->status = CONNECTION_NEEDED;
/*
- * Attempt connection to the next host, starting the
- * connect_timeout timer
+ * Restart the connect_timeout timer for the new host.
*/
- pqDropConnection(conn, true);
- conn->addr_cur = conn->connhost[conn->whichhost].addrlist;
- conn->status = CONNECTION_NEEDED;
- if (conn->connect_timeout != NULL)
+ if (timeout > 0)
finish_time = time(NULL) + timeout;
}
@@ -1940,27 +1998,29 @@ connectDBComplete(PGconn *conn)
/*
* This subroutine saves conn->errorMessage, which will be restored back by
- * restoreErrorMessage subroutine.
+ * 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;
}
-
- appendPQExpBufferStr(savedMessage,
- conn->errorMessage.data);
+ /* Clear whatever is in errorMessage now */
resetPQExpBuffer(&conn->errorMessage);
return true;
}
/*
- * Restores saved error messages back to conn->errorMessage.
+ * 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)
@@ -1968,6 +2028,11 @@ 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);
}
@@ -2001,6 +2066,8 @@ restoreErrorMessage(PGconn *conn, PQExpBuffer savedMessage)
PostgresPollingStatusType
PQconnectPoll(PGconn *conn)
{
+ bool reset_connection_state_machine = false;
+ bool need_new_connection = false;
PGresult *res;
char sebuf[256];
int optval;
@@ -2064,6 +2131,82 @@ PQconnectPoll(PGconn *conn)
keep_going: /* We will come back to here until there is
* nothing left to do. */
+
+ /* Time to advance to next address, or next host if no more addresses? */
+ if (conn->try_next_addr)
+ {
+ if (conn->addr_cur && conn->addr_cur->ai_next)
+ {
+ conn->addr_cur = conn->addr_cur->ai_next;
+ reset_connection_state_machine = true;
+ }
+ else
+ conn->try_next_host = true;
+ conn->try_next_addr = false;
+ }
+
+ /* Time to advance to next connhost[] entry? */
+ if (conn->try_next_host)
+ {
+ if (conn->whichhost + 1 >= conn->nconnhost)
+ {
+ /*
+ * Oops, no more hosts. An appropriate error message is already
+ * set up, so just set the right status.
+ */
+ goto error_return;
+ }
+ conn->whichhost++;
+ conn->addr_cur = conn->connhost[conn->whichhost].addrlist;
+ /* If no addresses for this host, just try the next one */
+ if (conn->addr_cur == NULL)
+ goto keep_going;
+ reset_connection_state_machine = true;
+ conn->try_next_host = false;
+ }
+
+ /* Reset connection state machine? */
+ if (reset_connection_state_machine)
+ {
+ /*
+ * (Re) initialize our connection control variables for a set of
+ * connection attempts to a single server address. These variables
+ * must persist across individual connection attempts, but we must
+ * reset them when we start to consider a new server.
+ */
+ conn->pversion = PG_PROTOCOL(3, 0);
+ conn->send_appname = true;
+#ifdef USE_SSL
+ /* initialize these values based on SSL mode */
+ conn->allow_ssl_try = (conn->sslmode[0] != 'd'); /* "disable" */
+ conn->wait_ssl_try = (conn->sslmode[0] == 'a'); /* "allow" */
+#endif
+
+ reset_connection_state_machine = false;
+ need_new_connection = true;
+ }
+
+ /* Force a new connection (perhaps to the same server as before)? */
+ if (need_new_connection)
+ {
+ /* Drop any existing connection */
+ pqDropConnection(conn, true);
+
+ /* Reset all state obtained from old server */
+ pqDropServerData(conn);
+
+ /* Drop any PGresult we might have, too */
+ conn->asyncStatus = PGASYNC_IDLE;
+ conn->xactStatus = PQTRANS_IDLE;
+ pqClearAsyncResult(conn);
+
+ /* Reset conn->status to put the state machine in the right state */
+ conn->status = CONNECTION_NEEDED;
+
+ need_new_connection = false;
+ }
+
+ /* Now try to advance the state machine for this connection */
switch (conn->status)
{
case CONNECTION_NEEDED:
@@ -2071,29 +2214,25 @@ keep_going: /* We will come back to here until there is
/*
* Try to initiate a connection to one of the addresses
* returned by pg_getaddrinfo_all(). conn->addr_cur is the
- * next one to try. We fail when we run out of addresses.
+ * next one to try.
+ *
+ * The extra level of braces here is historical. It's not
+ * worth reindenting this whole switch case to remove 'em.
*/
- for (;;)
{
- struct addrinfo *addr_cur;
+ struct addrinfo *addr_cur = conn->addr_cur;
/*
* Advance to next possible host, if we've tried all of
* the addresses for the current host.
*/
- if (conn->addr_cur == NULL)
+ if (addr_cur == NULL)
{
- if (++conn->whichhost >= conn->nconnhost)
- {
- conn->whichhost = 0;
- break;
- }
- conn->addr_cur =
- conn->connhost[conn->whichhost].addrlist;
+ conn->try_next_host = true;
+ goto keep_going;
}
/* Remember current address for possible error msg */
- addr_cur = conn->addr_cur;
memcpy(&conn->raddr.addr, addr_cur->ai_addr,
addr_cur->ai_addrlen);
conn->raddr.salen = addr_cur->ai_addrlen;
@@ -2102,33 +2241,35 @@ keep_going: /* We will come back to here until there is
if (conn->sock == PGINVALID_SOCKET)
{
/*
- * ignore socket() failure if we have more addresses
- * to try
+ * Silently ignore socket() failure if we have more
+ * addresses to try; this reduces useless chatter in
+ * cases where the address list includes both IPv4 and
+ * IPv6 but kernel only accepts one family.
*/
if (addr_cur->ai_next != NULL ||
conn->whichhost + 1 < conn->nconnhost)
{
- conn->addr_cur = addr_cur->ai_next;
- continue;
+ conn->try_next_addr = true;
+ goto keep_going;
}
appendPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not create socket: %s\n"),
SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
- break;
+ goto error_return;
}
/*
* Select socket options: no delay of outgoing data for
- * TCP sockets, nonblock mode, close-on-exec. Fail if any
- * of this fails.
+ * TCP sockets, nonblock mode, close-on-exec. Try the
+ * next address if any of this fails.
*/
if (!IS_AF_UNIX(addr_cur->ai_family))
{
if (!connectNoDelay(conn))
{
- pqDropConnection(conn, true);
- conn->addr_cur = addr_cur->ai_next;
- continue;
+ /* error message already created */
+ conn->try_next_addr = true;
+ goto keep_going;
}
}
if (!pg_set_noblock(conn->sock))
@@ -2136,9 +2277,8 @@ keep_going: /* We will come back to here until there is
appendPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not set socket to nonblocking mode: %s\n"),
SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
- pqDropConnection(conn, true);
- conn->addr_cur = addr_cur->ai_next;
- continue;
+ conn->try_next_addr = true;
+ goto keep_going;
}
#ifdef F_SETFD
@@ -2147,9 +2287,8 @@ keep_going: /* We will come back to here until there is
appendPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not set socket to close-on-exec mode: %s\n"),
SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
- pqDropConnection(conn, true);
- conn->addr_cur = addr_cur->ai_next;
- continue;
+ conn->try_next_addr = true;
+ goto keep_going;
}
#endif /* F_SETFD */
@@ -2195,9 +2334,8 @@ keep_going: /* We will come back to here until there is
if (err)
{
- pqDropConnection(conn, true);
- conn->addr_cur = addr_cur->ai_next;
- continue;
+ conn->try_next_addr = true;
+ goto keep_going;
}
}
@@ -2276,25 +2414,13 @@ keep_going: /* We will come back to here until there is
}
/*
- * This connection failed --- set up error report, then
- * close socket (do it this way in case close() affects
- * the value of errno...). We will ignore the connect()
- * failure and keep going if there are more addresses.
+ * This connection failed. Add the error report to
+ * conn->errorMessage, then try the next address if any.
*/
connectFailureMessage(conn, SOCK_ERRNO);
- pqDropConnection(conn, true);
-
- /*
- * Try the next address, if any.
- */
- conn->addr_cur = addr_cur->ai_next;
- } /* loop over addresses */
-
- /*
- * Oops, no more addresses. An appropriate error message is
- * already set up, so just set the right status.
- */
- goto error_return;
+ conn->try_next_addr = true;
+ goto keep_going;
+ }
}
case CONNECTION_STARTED:
@@ -2327,20 +2453,13 @@ keep_going: /* We will come back to here until there is
* error message.
*/
connectFailureMessage(conn, optval);
- pqDropConnection(conn, true);
/*
- * If more addresses remain, keep trying, just as in the
- * case where connect() returned failure immediately.
+ * Try the next address if any, just as in the case where
+ * connect() returned failure immediately.
*/
- if (conn->addr_cur->ai_next != NULL ||
- conn->whichhost + 1 < conn->nconnhost)
- {
- conn->addr_cur = conn->addr_cur->ai_next;
- conn->status = CONNECTION_NEEDED;
- goto keep_going;
- }
- goto error_return;
+ conn->try_next_addr = true;
+ goto keep_going;
}
/* Fill in the client address */
@@ -2615,12 +2734,13 @@ keep_going: /* We will come back to here until there is
{
/* only retry once */
conn->allow_ssl_try = false;
- /* Must drop the old connection */
- pqDropConnection(conn, true);
- conn->status = CONNECTION_NEEDED;
+ need_new_connection = true;
goto keep_going;
}
+ /* Else it's a hard failure */
+ goto error_return;
}
+ /* Else, return POLLING_READING or POLLING_WRITING status */
return pollres;
#else /* !USE_SSL */
/* can't get here */
@@ -2727,9 +2847,7 @@ keep_going: /* We will come back to here until there is
if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
{
conn->pversion = PG_PROTOCOL(2, 0);
- /* Must drop the old connection */
- pqDropConnection(conn, true);
- conn->status = CONNECTION_NEEDED;
+ need_new_connection = true;
goto keep_going;
}
@@ -2780,6 +2898,9 @@ keep_going: /* We will come back to here until there is
/* OK, we read the message; mark data consumed */
conn->inStart = conn->inCursor;
+ /* Check to see if we should mention pgpassfile */
+ pgpassfileWarning(conn);
+
#ifdef USE_SSL
/*
@@ -2793,9 +2914,7 @@ keep_going: /* We will come back to here until there is
{
/* only retry once */
conn->wait_ssl_try = false;
- /* Must drop the old connection */
- pqDropConnection(conn, true);
- conn->status = CONNECTION_NEEDED;
+ need_new_connection = true;
goto keep_going;
}
@@ -2804,14 +2923,13 @@ keep_going: /* We will come back to here until there is
* then do a non-SSL retry
*/
if (conn->sslmode[0] == 'p' /* "prefer" */
- && conn->allow_ssl_try
+ && conn->ssl_in_use
+ && conn->allow_ssl_try /* redundant? */
&& !conn->wait_ssl_try) /* redundant? */
{
/* only retry once */
conn->allow_ssl_try = false;
- /* Must drop the old connection */
- pqDropConnection(conn, true);
- conn->status = CONNECTION_NEEDED;
+ need_new_connection = true;
goto keep_going;
}
#endif
@@ -2947,9 +3065,7 @@ keep_going: /* We will come back to here until there is
{
PQclear(res);
conn->send_appname = false;
- /* Must drop the old connection */
- pqDropConnection(conn, true);
- conn->status = CONNECTION_NEEDED;
+ need_new_connection = true;
goto keep_going;
}
}
@@ -2978,17 +3094,21 @@ keep_going: /* We will come back to here until there is
/*
* If a read-write connection is required, see if we have one.
+ *
+ * Servers before 7.4 lack the transaction_read_only GUC, but
+ * by the same token they don't have any read-only mode, so we
+ * may just skip the test in that case.
*/
- if (conn->target_session_attrs != NULL &&
+ if (conn->sversion >= 70400 &&
+ 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 lose error
- * messages related to previous hosts we have tried to
- * connect and failed.
+ * Save existing error messages across the PQsendQuery
+ * attempt. This is necessary because PQsendQuery is
+ * going to reset conn->errorMessage, so we would lose
+ * error messages related to previous hosts we have tried
+ * and failed to connect to.
*/
if (!saveErrorMessage(conn, &savedMessage))
goto error_return;
@@ -3041,9 +3161,16 @@ keep_going: /* We will come back to here until there is
}
/*
- * If a read-write connection is requested check for same.
+ * If a read-write connection is required, see if we have one.
+ * (This should match the stanza in the CONNECTION_AUTH_OK case
+ * above.)
+ *
+ * Servers before 7.4 lack the transaction_read_only GUC, but by
+ * the same token they don't have any read-only mode, so we may
+ * just skip the test in that case.
*/
- if (conn->target_session_attrs != NULL &&
+ if (conn->sversion >= 70400 &&
+ conn->target_session_attrs != NULL &&
strcmp(conn->target_session_attrs, "read-write") == 0)
{
if (!saveErrorMessage(conn, &savedMessage))
@@ -3077,7 +3204,6 @@ keep_going: /* We will come back to here until there is
if (PQisBusy(conn))
{
conn->status = CONNECTION_CONSUME;
- restoreErrorMessage(conn, &savedMessage);
return PGRES_POLLING_READING;
}
@@ -3127,9 +3253,14 @@ keep_going: /* We will come back to here until there is
val = PQgetvalue(res, 0, 0);
if (strncmp(val, "on", 2) == 0)
{
+ /* Not writable; fail this connection. */
const char *displayed_host;
const char *displayed_port;
+ PQclear(res);
+ restoreErrorMessage(conn, &savedMessage);
+
+ /* Append error report to conn->errorMessage. */
if (conn->connhost[conn->whichhost].type == CHT_HOST_ADDRESS)
displayed_host = conn->connhost[conn->whichhost].hostaddr;
else
@@ -3138,30 +3269,25 @@ keep_going: /* We will come back to here until there is
if (displayed_port == NULL || displayed_port[0] == '\0')
displayed_port = DEF_PGPORT_STR;
- PQclear(res);
- restoreErrorMessage(conn, &savedMessage);
-
- /* Not writable; close connection. */
appendPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not make a writable "
"connection to server "
"\"%s:%s\"\n"),
displayed_host, displayed_port);
+
+ /* Close connection politely. */
conn->status = CONNECTION_OK;
sendTerminateConn(conn);
- pqDropConnection(conn, true);
-
- /* Skip any remaining addresses for this host. */
- conn->addr_cur = NULL;
- if (conn->whichhost + 1 < conn->nconnhost)
- {
- conn->status = CONNECTION_NEEDED;
- goto keep_going;
- }
- /* No more addresses to try. So we fail. */
- goto error_return;
+ /*
+ * Try next host if any, but we don't want to consider
+ * additional addresses for this host.
+ */
+ conn->try_next_host = true;
+ goto keep_going;
}
+
+ /* Session is read-write, so we're good. */
PQclear(res);
termPQExpBuffer(&savedMessage);
@@ -3184,6 +3310,7 @@ keep_going: /* We will come back to here until there is
PQclear(res);
restoreErrorMessage(conn, &savedMessage);
+ /* Append error report to conn->errorMessage. */
if (conn->connhost[conn->whichhost].type == CHT_HOST_ADDRESS)
displayed_host = conn->connhost[conn->whichhost].hostaddr;
else
@@ -3195,20 +3322,14 @@ keep_going: /* We will come back to here until there is
libpq_gettext("test \"SHOW transaction_read_only\" failed "
"on server \"%s:%s\"\n"),
displayed_host, displayed_port);
+
+ /* Close connection politely. */
conn->status = CONNECTION_OK;
sendTerminateConn(conn);
- pqDropConnection(conn, true);
-
- if (conn->addr_cur->ai_next != NULL ||
- conn->whichhost + 1 < conn->nconnhost)
- {
- conn->addr_cur = conn->addr_cur->ai_next;
- conn->status = CONNECTION_NEEDED;
- goto keep_going;
- }
- /* No more addresses to try. So we fail. */
- goto error_return;
+ /* Try next address */
+ conn->try_next_addr = true;
+ goto keep_going;
}
default:
@@ -3223,8 +3344,6 @@ keep_going: /* We will come back to here until there is
error_return:
- pgpassfileWarning(conn);
-
/*
* We used to close the socket at this point, but that makes it awkward
* for those above us if they wish to remove this socket from their own
@@ -3352,14 +3471,6 @@ makeEmptyPGconn(void)
conn->verbosity = PQERRORS_DEFAULT;
conn->show_context = PQSHOW_CONTEXT_ERRORS;
conn->sock = PGINVALID_SOCKET;
- conn->auth_req_received = false;
- conn->password_needed = false;
- conn->pgpassfile_used = false;
-#ifdef USE_SSL
- conn->allow_ssl_try = true;
- conn->wait_ssl_try = false;
- conn->ssl_in_use = false;
-#endif
/*
* We try to send at least 8K at a time, which is the usual size of pipe
@@ -3581,9 +3692,9 @@ sendTerminateConn(PGconn *conn)
static void
closePGconn(PGconn *conn)
{
- PGnotify *notify;
- pgParameterStatus *pstatus;
-
+ /*
+ * If possible, send Terminate message to close the connection politely.
+ */
sendTerminateConn(conn);
/*
@@ -3600,31 +3711,13 @@ closePGconn(PGconn *conn)
pqDropConnection(conn, true);
conn->status = CONNECTION_BAD; /* Well, not really _bad_ - just absent */
conn->asyncStatus = PGASYNC_IDLE;
+ conn->xactStatus = PQTRANS_IDLE;
pqClearAsyncResult(conn); /* deallocate result */
resetPQExpBuffer(&conn->errorMessage);
release_all_addrinfo(conn);
- notify = conn->notifyHead;
- while (notify != NULL)
- {
- PGnotify *prev = notify;
-
- notify = notify->next;
- free(prev);
- }
- conn->notifyHead = conn->notifyTail = NULL;
- pstatus = conn->pstatus;
- while (pstatus != NULL)
- {
- pgParameterStatus *prev = pstatus;
-
- pstatus = pstatus->next;
- free(prev);
- }
- conn->pstatus = NULL;
- if (conn->lobjfuncs)
- free(conn->lobjfuncs);
- conn->lobjfuncs = NULL;
+ /* Reset all state obtained from server, too */
+ pqDropServerData(conn);
}
/*
@@ -6505,7 +6598,9 @@ pgpassfileWarning(PGconn *conn)
{
/* If it was 'invalid authorization', add pgpassfile mention */
/* only works with >= 9.0 servers */
- if (conn->pgpassfile_used && conn->password_needed && conn->result)
+ if (conn->password_needed &&
+ conn->connhost[conn->whichhost].password != NULL &&
+ conn->result)
{
const char *sqlstate = PQresultErrorField(conn->result,
PG_DIAG_SQLSTATE);
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 4a836b186ef..bc60373cb0f 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -290,6 +290,7 @@ typedef struct pgDataValue
const char *value; /* data value, without zero-termination */
} PGdataValue;
+/* Host address type enum for struct pg_conn_host */
typedef enum pg_conn_host_type
{
CHT_HOST_NAME,
@@ -298,20 +299,19 @@ typedef enum pg_conn_host_type
} pg_conn_host_type;
/*
- * pg_conn_host stores all information about one of possibly several hosts
- * mentioned in the connection string. Derived by splitting the pghost
- * on the comma character and then parsing each segment.
+ * pg_conn_host stores all information about each of possibly several hosts
+ * mentioned in the connection string. Most fields are derived by splitting
+ * the relevant connection parameter (e.g., pghost) at commas.
*/
typedef struct pg_conn_host
{
- pg_conn_host_type type; /* type of host */
+ pg_conn_host_type type; /* type of host address */
char *host; /* host name or socket path */
- char *hostaddr; /* host address */
- char *port; /* port number for this host; if not NULL,
- * overrides the PGConn's pgport */
+ char *hostaddr; /* host numeric IP address */
+ char *port; /* port number (always provided) */
char *password; /* password for this host, read from the
- * password file. only set if the PGconn's
- * pgpass field is NULL. */
+ * password file; NULL if not sought or not
+ * found in password file. */
struct addrinfo *addrlist; /* list of possible backend addresses */
} pg_conn_host;
@@ -325,12 +325,13 @@ struct pg_conn
char *pghost; /* the machine on which the server is running,
* or a path to a UNIX-domain socket, or a
* comma-separated list of machines and/or
- * paths, optionally with port suffixes; if
- * NULL, use DEFAULT_PGSOCKET_DIR */
+ * paths; if NULL, use DEFAULT_PGSOCKET_DIR */
char *pghostaddr; /* the numeric IP address of the machine on
- * which the server is running. Takes
- * precedence over above. */
- char *pgport; /* the server's communication port number */
+ * which the server is running, or a
+ * comma-separated list of same. Takes
+ * precedence over pghost. */
+ char *pgport; /* the server's communication port number, or
+ * a comma-separated list of ports */
char *pgtty; /* tty on which the backend messages is
* displayed (OBSOLETE, NOT USED) */
char *connect_timeout; /* connection timeout (numeric string) */
@@ -392,9 +393,9 @@ struct pg_conn
PGnotify *notifyTail; /* newest unreported Notify msg */
/* Support for multiple hosts in connection string */
- int nconnhost; /* # of possible hosts */
- int whichhost; /* host we're currently considering */
- pg_conn_host *connhost; /* details about each possible host */
+ int nconnhost; /* # of hosts named in conn string */
+ int whichhost; /* host we're currently trying/connected to */
+ pg_conn_host *connhost; /* details about each named host */
/* Connection data */
pgsocket sock; /* FD for socket, PGINVALID_SOCKET if
@@ -405,11 +406,12 @@ struct pg_conn
int sversion; /* server version, e.g. 70401 for 7.4.1 */
bool auth_req_received; /* true if any type of auth req received */
bool password_needed; /* true if server demanded a password */
- bool pgpassfile_used; /* true if password is from pgpassfile */
bool sigpipe_so; /* have we masked SIGPIPE via SO_NOSIGPIPE? */
bool sigpipe_flag; /* can we mask SIGPIPE via MSG_NOSIGNAL? */
/* Transient state needed while establishing connection */
+ bool try_next_addr; /* time to advance to next address/host? */
+ bool try_next_host; /* time to advance to next connhost[]? */
struct addrinfo *addr_cur; /* backend address currently being tried */
PGSetenvStatusType setenv_state; /* for 2.0 protocol only */
const PQEnvironmentOption *next_eo;