Commit 5ca00774 authored by Tom Lane's avatar Tom Lane

In libpq, don't look up all the hostnames at once.

Historically, we looked up the target hostname in connectDBStart, so that
PQconnectPoll did not need to do DNS name resolution.  The patches that
added multiple-target-host support to libpq preserved this division of
labor; but it's really nonsensical now, because it means that if any one
of the target hosts fails to resolve in DNS, the connection fails.  That
negates the no-single-point-of-failure goal of the feature.  Additionally,
DNS lookups aren't exactly cheap, but the code did them all even if the
first connection attempt succeeds.

Hence, rearrange so that PQconnectPoll does the lookups, and only looks
up a hostname when it's time to try that host.  This does mean that
PQconnectPoll could block on a DNS lookup --- but if you wanted to avoid
that, you should be using hostaddr, as the documentation has always
specified.  It seems fairly unlikely that any applications would really
care whether the lookup occurs inside PQconnectStart or PQconnectPoll.

In addition to calling out that fact explicitly, do some other minor
wordsmithing in the docs around the multiple-target-host feature.

Since this seems like a bug in the multiple-target-host feature,
backpatch to v10 where that was introduced.  In the back branches,
avoid moving any existing fields of struct pg_conn, just in case
any third-party code is looking into that struct.

Tom Lane, reviewed by Fabien Coelho

Discussion: https://postgr.es/m/4913.1533827102@sss.pgh.pa.us
parent 2d41d914
...@@ -303,9 +303,9 @@ PostgresPollingStatusType PQconnectPoll(PGconn *conn); ...@@ -303,9 +303,9 @@ PostgresPollingStatusType PQconnectPoll(PGconn *conn);
<itemizedlist> <itemizedlist>
<listitem> <listitem>
<para> <para>
The <literal>hostaddr</literal> and <literal>host</literal> parameters are used appropriately to ensure that The <literal>hostaddr</literal> parameter must be used appropriately
name and reverse name queries are not made. See the documentation of to prevent DNS queries from being made. See the documentation of
these parameters in <xref linkend="libpq-paramkeywords"/> for details. this parameter in <xref linkend="libpq-paramkeywords"/> for details.
</para> </para>
</listitem> </listitem>
...@@ -318,7 +318,7 @@ PostgresPollingStatusType PQconnectPoll(PGconn *conn); ...@@ -318,7 +318,7 @@ PostgresPollingStatusType PQconnectPoll(PGconn *conn);
<listitem> <listitem>
<para> <para>
You ensure that the socket is in the appropriate state You must ensure that the socket is in the appropriate state
before calling <function>PQconnectPoll</function>, as described below. before calling <function>PQconnectPoll</function>, as described below.
</para> </para>
</listitem> </listitem>
...@@ -326,24 +326,27 @@ PostgresPollingStatusType PQconnectPoll(PGconn *conn); ...@@ -326,24 +326,27 @@ PostgresPollingStatusType PQconnectPoll(PGconn *conn);
</para> </para>
<para> <para>
Note: use of <function>PQconnectStartParams</function> is analogous to To begin a nonblocking connection request,
<function>PQconnectStart</function> shown below. call <function>PQconnectStart</function>
or <function>PQconnectStartParams</function>. If the result is null,
then <application>libpq</application> has been unable to allocate a
new <structname>PGconn</structname> structure. Otherwise, a
valid <structname>PGconn</structname> pointer is returned (though not
yet representing a valid connection to the database). Next
call <literal>PQstatus(conn)</literal>. If the result
is <symbol>CONNECTION_BAD</symbol>, the connection attempt has already
failed, typically because of invalid connection parameters.
</para> </para>
<para> <para>
To begin a nonblocking connection request, call <literal>conn = PQconnectStart("<replaceable>connection_info_string</replaceable>")</literal>. If <function>PQconnectStart</function>
If <varname>conn</varname> is null, then <application>libpq</application> has been unable to allocate a new <structname>PGconn</structname> or <function>PQconnectStartParams</function> succeeds, the next stage
structure. Otherwise, a valid <structname>PGconn</structname> pointer is returned (though not yet is to poll <application>libpq</application> so that it can proceed with
representing a valid connection to the database). On return from the connection sequence.
<function>PQconnectStart</function>, call <literal>status = PQstatus(conn)</literal>. If <varname>status</varname> equals
<symbol>CONNECTION_BAD</symbol>, <function>PQconnectStart</function> has failed.
</para>
<para>
If <function>PQconnectStart</function> succeeds, the next stage is to poll
<application>libpq</application> so that it can proceed with the connection sequence.
Use <function>PQsocket(conn)</function> to obtain the descriptor of the Use <function>PQsocket(conn)</function> to obtain the descriptor of the
socket underlying the database connection. socket underlying the database connection.
(Caution: do not assume that the socket remains the same
across <function>PQconnectPoll</function> calls.)
Loop thus: If <function>PQconnectPoll(conn)</function> last returned Loop thus: If <function>PQconnectPoll(conn)</function> last returned
<symbol>PGRES_POLLING_READING</symbol>, wait until the socket is ready to <symbol>PGRES_POLLING_READING</symbol>, wait until the socket is ready to
read (as indicated by <function>select()</function>, <function>poll()</function>, or read (as indicated by <function>select()</function>, <function>poll()</function>, or
...@@ -352,9 +355,8 @@ PostgresPollingStatusType PQconnectPoll(PGconn *conn); ...@@ -352,9 +355,8 @@ PostgresPollingStatusType PQconnectPoll(PGconn *conn);
Conversely, if <function>PQconnectPoll(conn)</function> last returned Conversely, if <function>PQconnectPoll(conn)</function> last returned
<symbol>PGRES_POLLING_WRITING</symbol>, wait until the socket is ready <symbol>PGRES_POLLING_WRITING</symbol>, wait until the socket is ready
to write, then call <function>PQconnectPoll(conn)</function> again. to write, then call <function>PQconnectPoll(conn)</function> again.
If you have yet to call On the first iteration, i.e. if you have yet to call
<function>PQconnectPoll</function>, i.e., just after the call to <function>PQconnectPoll</function>, behave as if it last returned
<function>PQconnectStart</function>, behave as if it last returned
<symbol>PGRES_POLLING_WRITING</symbol>. Continue this loop until <symbol>PGRES_POLLING_WRITING</symbol>. Continue this loop until
<function>PQconnectPoll(conn)</function> returns <function>PQconnectPoll(conn)</function> returns
<symbol>PGRES_POLLING_FAILED</symbol>, indicating the connection procedure <symbol>PGRES_POLLING_FAILED</symbol>, indicating the connection procedure
...@@ -479,10 +481,12 @@ switch(PQstatus(conn)) ...@@ -479,10 +481,12 @@ switch(PQstatus(conn))
</para> </para>
<para> <para>
Note that if <function>PQconnectStart</function> returns a non-null pointer, you must call Note that when <function>PQconnectStart</function>
<function>PQfinish</function> when you are finished with it, in order to dispose of or <function>PQconnectStartParams</function> returns a non-null
the structure and any associated memory blocks. This must be done even if pointer, you must call <function>PQfinish</function> when you are
the connection attempt fails or is abandoned. finished with it, in order to dispose of the structure and any
associated memory blocks. This must be done even if the connection
attempt fails or is abandoned.
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
...@@ -913,7 +917,8 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname ...@@ -913,7 +917,8 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
It is possible to specify multiple hosts to connect to, so that they are It is possible to specify multiple hosts to connect to, so that they are
tried in the given order. In the Keyword/Value format, the <literal>host</literal>, tried in the given order. In the Keyword/Value format, the <literal>host</literal>,
<literal>hostaddr</literal>, and <literal>port</literal> options accept a comma-separated <literal>hostaddr</literal>, and <literal>port</literal> options accept a comma-separated
list of values. The same number of elements must be given in each option, such list of values. The same number of elements must be given in each
option that is specified, such
that e.g. the first <literal>hostaddr</literal> corresponds to the first host name, that e.g. the first <literal>hostaddr</literal> corresponds to the first host name,
the second <literal>hostaddr</literal> corresponds to the second host name, and so the second <literal>hostaddr</literal> corresponds to the second host name, and so
forth. As an exception, if only one <literal>port</literal> is specified, it forth. As an exception, if only one <literal>port</literal> is specified, it
...@@ -922,9 +927,13 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname ...@@ -922,9 +927,13 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
<para> <para>
In the connection URI format, you can list multiple <literal>host:port</literal> pairs In the connection URI format, you can list multiple <literal>host:port</literal> pairs
separated by commas, in the <literal>host</literal> component of the URI. In either separated by commas, in the <literal>host</literal> component of the URI.
format, a single host name can also translate to multiple network addresses. A </para>
common example of this is a host that has both an IPv4 and an IPv6 address.
<para>
In either format, a single host name can translate to multiple network
addresses. A common example of this is a host that has both an IPv4 and
an IPv6 address.
</para> </para>
<para> <para>
...@@ -958,9 +967,8 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname ...@@ -958,9 +967,8 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
Name of host to connect to.<indexterm><primary>host name</primary></indexterm> Name of host to connect to.<indexterm><primary>host name</primary></indexterm>
If a host name begins with a slash, it specifies Unix-domain If a host name begins with a slash, it specifies Unix-domain
communication rather than TCP/IP communication; the value is the communication rather than TCP/IP communication; the value is the
name of the directory in which the socket file is stored. If name of the directory in which the socket file is stored.
multiple host names are specified, each will be tried in turn in The default behavior when <literal>host</literal> is
the order given. The default behavior when <literal>host</literal> is
not specified, or is empty, is to connect to a Unix-domain not specified, or is empty, is to connect to a Unix-domain
socket<indexterm><primary>Unix domain socket</primary></indexterm> in socket<indexterm><primary>Unix domain socket</primary></indexterm> in
<filename>/tmp</filename> (or whatever socket directory was specified <filename>/tmp</filename> (or whatever socket directory was specified
...@@ -997,8 +1005,12 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname ...@@ -997,8 +1005,12 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
<itemizedlist> <itemizedlist>
<listitem> <listitem>
<para> <para>
If <literal>host</literal> is specified without <literal>hostaddr</literal>, If <literal>host</literal> is specified
a host name lookup occurs. without <literal>hostaddr</literal>, a host name lookup occurs.
(When using <function>PQconnectPoll</function>, the lookup occurs
when <function>PQconnectPoll</function> first considers this host
name, and it may cause <function>PQconnectPoll</function> to block
for a significant amount of time.)
</para> </para>
</listitem> </listitem>
<listitem> <listitem>
......
...@@ -360,7 +360,7 @@ static PGconn *makeEmptyPGconn(void); ...@@ -360,7 +360,7 @@ static PGconn *makeEmptyPGconn(void);
static bool fillPGconn(PGconn *conn, PQconninfoOption *connOptions); static bool fillPGconn(PGconn *conn, PQconninfoOption *connOptions);
static void freePGconn(PGconn *conn); static void freePGconn(PGconn *conn);
static void closePGconn(PGconn *conn); static void closePGconn(PGconn *conn);
static void release_all_addrinfo(PGconn *conn); static void release_conn_addrinfo(PGconn *conn);
static void sendTerminateConn(PGconn *conn); static void sendTerminateConn(PGconn *conn);
static PQconninfoOption *conninfo_init(PQExpBuffer errorMessage); static PQconninfoOption *conninfo_init(PQExpBuffer errorMessage);
static PQconninfoOption *parse_connection_string(const char *conninfo, static PQconninfoOption *parse_connection_string(const char *conninfo,
...@@ -1742,10 +1742,6 @@ setKeepalivesWin32(PGconn *conn) ...@@ -1742,10 +1742,6 @@ setKeepalivesWin32(PGconn *conn)
static int static int
connectDBStart(PGconn *conn) connectDBStart(PGconn *conn)
{ {
char portstr[MAXPGPATH];
int ret;
int i;
if (!conn) if (!conn)
return 0; return 0;
...@@ -1764,101 +1760,6 @@ connectDBStart(PGconn *conn) ...@@ -1764,101 +1760,6 @@ connectDBStart(PGconn *conn)
*/ */
resetPQExpBuffer(&conn->errorMessage); resetPQExpBuffer(&conn->errorMessage);
/*
* Look up socket addresses for each possible host using
* pg_getaddrinfo_all.
*/
for (i = 0; i < conn->nconnhost; ++i)
{
pg_conn_host *ch = &conn->connhost[i];
struct addrinfo hint;
int thisport;
/* Initialize hint structure */
MemSet(&hint, 0, sizeof(hint));
hint.ai_socktype = SOCK_STREAM;
hint.ai_family = AF_UNSPEC;
/* Figure out the port number we're going to use. */
if (ch->port == NULL || ch->port[0] == '\0')
thisport = DEF_PGPORT;
else
{
thisport = atoi(ch->port);
if (thisport < 1 || thisport > 65535)
{
appendPQExpBuffer(&conn->errorMessage,
libpq_gettext("invalid port number: \"%s\"\n"),
ch->port);
conn->options_valid = false;
goto connect_errReturn;
}
}
snprintf(portstr, sizeof(portstr), "%d", thisport);
/* Use pg_getaddrinfo_all() to resolve the address */
ret = 1;
switch (ch->type)
{
case CHT_HOST_NAME:
ret = pg_getaddrinfo_all(ch->host, portstr, &hint, &ch->addrlist);
if (ret || !ch->addrlist)
appendPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not translate host name \"%s\" to address: %s\n"),
ch->host, gai_strerror(ret));
break;
case CHT_HOST_ADDRESS:
hint.ai_flags = AI_NUMERICHOST;
ret = pg_getaddrinfo_all(ch->hostaddr, portstr, &hint, &ch->addrlist);
if (ret || !ch->addrlist)
appendPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not parse network address \"%s\": %s\n"),
ch->hostaddr, gai_strerror(ret));
break;
case CHT_UNIX_SOCKET:
#ifdef HAVE_UNIX_SOCKETS
hint.ai_family = AF_UNIX;
UNIXSOCK_PATH(portstr, thisport, ch->host);
if (strlen(portstr) >= UNIXSOCK_PATH_BUFLEN)
{
appendPQExpBuffer(&conn->errorMessage,
libpq_gettext("Unix-domain socket path \"%s\" is too long (maximum %d bytes)\n"),
portstr,
(int) (UNIXSOCK_PATH_BUFLEN - 1));
conn->options_valid = false;
goto connect_errReturn;
}
/*
* NULL hostname tells pg_getaddrinfo_all to parse the service
* name as a Unix-domain socket path.
*/
ret = pg_getaddrinfo_all(NULL, portstr, &hint, &ch->addrlist);
if (ret || !ch->addrlist)
appendPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not translate Unix-domain socket path \"%s\" to address: %s\n"),
portstr, gai_strerror(ret));
break;
#else
Assert(false);
conn->options_valid = false;
goto connect_errReturn;
#endif
}
if (ret || !ch->addrlist)
{
if (ch->addrlist)
{
pg_freeaddrinfo_all(hint.ai_family, ch->addrlist);
ch->addrlist = NULL;
}
conn->options_valid = false;
goto connect_errReturn;
}
}
/* /*
* Set up to try to connect to the first host. (Setting whichhost = -1 is * 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 * a bit of a cheat, but PQconnectPoll will advance it to 0 before
...@@ -2157,6 +2058,12 @@ keep_going: /* We will come back to here until there is ...@@ -2157,6 +2058,12 @@ keep_going: /* We will come back to here until there is
/* Time to advance to next connhost[] entry? */ /* Time to advance to next connhost[] entry? */
if (conn->try_next_host) if (conn->try_next_host)
{ {
pg_conn_host *ch;
struct addrinfo hint;
int thisport;
int ret;
char portstr[MAXPGPATH];
if (conn->whichhost + 1 >= conn->nconnhost) if (conn->whichhost + 1 >= conn->nconnhost)
{ {
/* /*
...@@ -2166,10 +2073,100 @@ keep_going: /* We will come back to here until there is ...@@ -2166,10 +2073,100 @@ keep_going: /* We will come back to here until there is
goto error_return; goto error_return;
} }
conn->whichhost++; conn->whichhost++;
conn->addr_cur = conn->connhost[conn->whichhost].addrlist;
/* If no addresses for this host, just try the next one */ /* Drop any address info for previous host */
if (conn->addr_cur == NULL) release_conn_addrinfo(conn);
/*
* Look up info for the new host. On failure, log the problem in
* conn->errorMessage, then loop around to try the next host. (Note
* we don't clear try_next_host until we've succeeded.)
*/
ch = &conn->connhost[conn->whichhost];
/* Initialize hint structure */
MemSet(&hint, 0, sizeof(hint));
hint.ai_socktype = SOCK_STREAM;
conn->addrlist_family = hint.ai_family = AF_UNSPEC;
/* Figure out the port number we're going to use. */
if (ch->port == NULL || ch->port[0] == '\0')
thisport = DEF_PGPORT;
else
{
thisport = atoi(ch->port);
if (thisport < 1 || thisport > 65535)
{
appendPQExpBuffer(&conn->errorMessage,
libpq_gettext("invalid port number: \"%s\"\n"),
ch->port);
goto keep_going;
}
}
snprintf(portstr, sizeof(portstr), "%d", thisport);
/* Use pg_getaddrinfo_all() to resolve the address */
switch (ch->type)
{
case CHT_HOST_NAME:
ret = pg_getaddrinfo_all(ch->host, portstr, &hint,
&conn->addrlist);
if (ret || !conn->addrlist)
{
appendPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not translate host name \"%s\" to address: %s\n"),
ch->host, gai_strerror(ret));
goto keep_going;
}
break;
case CHT_HOST_ADDRESS:
hint.ai_flags = AI_NUMERICHOST;
ret = pg_getaddrinfo_all(ch->hostaddr, portstr, &hint,
&conn->addrlist);
if (ret || !conn->addrlist)
{
appendPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not parse network address \"%s\": %s\n"),
ch->hostaddr, gai_strerror(ret));
goto keep_going; goto keep_going;
}
break;
case CHT_UNIX_SOCKET:
#ifdef HAVE_UNIX_SOCKETS
conn->addrlist_family = hint.ai_family = AF_UNIX;
UNIXSOCK_PATH(portstr, thisport, ch->host);
if (strlen(portstr) >= UNIXSOCK_PATH_BUFLEN)
{
appendPQExpBuffer(&conn->errorMessage,
libpq_gettext("Unix-domain socket path \"%s\" is too long (maximum %d bytes)\n"),
portstr,
(int) (UNIXSOCK_PATH_BUFLEN - 1));
goto keep_going;
}
/*
* NULL hostname tells pg_getaddrinfo_all to parse the service
* name as a Unix-domain socket path.
*/
ret = pg_getaddrinfo_all(NULL, portstr, &hint,
&conn->addrlist);
if (ret || !conn->addrlist)
{
appendPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not translate Unix-domain socket path \"%s\" to address: %s\n"),
portstr, gai_strerror(ret));
goto keep_going;
}
#else
Assert(false);
#endif
break;
}
/* OK, scan this addrlist for a working server address */
conn->addr_cur = conn->addrlist;
reset_connection_state_machine = true; reset_connection_state_machine = true;
conn->try_next_host = false; conn->try_next_host = false;
} }
...@@ -3134,8 +3131,8 @@ keep_going: /* We will come back to here until there is ...@@ -3134,8 +3131,8 @@ keep_going: /* We will come back to here until there is
return PGRES_POLLING_READING; return PGRES_POLLING_READING;
} }
/* We can release the address lists now. */ /* We can release the address list now. */
release_all_addrinfo(conn); release_conn_addrinfo(conn);
/* We are open for business! */ /* We are open for business! */
conn->status = CONNECTION_OK; conn->status = CONNECTION_OK;
...@@ -3197,8 +3194,8 @@ keep_going: /* We will come back to here until there is ...@@ -3197,8 +3194,8 @@ keep_going: /* We will come back to here until there is
return PGRES_POLLING_READING; return PGRES_POLLING_READING;
} }
/* We can release the address lists now. */ /* We can release the address list now. */
release_all_addrinfo(conn); release_conn_addrinfo(conn);
/* We are open for business! */ /* We are open for business! */
conn->status = CONNECTION_OK; conn->status = CONNECTION_OK;
...@@ -3227,6 +3224,9 @@ keep_going: /* We will come back to here until there is ...@@ -3227,6 +3224,9 @@ keep_going: /* We will come back to here until there is
goto keep_going; goto keep_going;
} }
/* We can release the address list now. */
release_conn_addrinfo(conn);
/* We are open for business! */ /* We are open for business! */
conn->status = CONNECTION_OK; conn->status = CONNECTION_OK;
return PGRES_POLLING_OK; return PGRES_POLLING_OK;
...@@ -3300,9 +3300,6 @@ keep_going: /* We will come back to here until there is ...@@ -3300,9 +3300,6 @@ keep_going: /* We will come back to here until there is
PQclear(res); PQclear(res);
termPQExpBuffer(&savedMessage); termPQExpBuffer(&savedMessage);
/* We can release the address lists now. */
release_all_addrinfo(conn);
/* /*
* Finish reading any remaining messages before being * Finish reading any remaining messages before being
* considered as ready. * considered as ready.
...@@ -3639,32 +3636,18 @@ freePGconn(PGconn *conn) ...@@ -3639,32 +3636,18 @@ freePGconn(PGconn *conn)
} }
/* /*
* release_all_addrinfo * release_conn_addrinfo
* - free addrinfo of all hostconn elements. * - Free any addrinfo list in the PGconn.
*/ */
static void static void
release_all_addrinfo(PGconn *conn) release_conn_addrinfo(PGconn *conn)
{ {
if (conn->connhost != NULL) if (conn->addrlist)
{
int i;
for (i = 0; i < conn->nconnhost; ++i)
{ {
int family = AF_UNSPEC; pg_freeaddrinfo_all(conn->addrlist_family, conn->addrlist);
conn->addrlist = NULL;
#ifdef HAVE_UNIX_SOCKETS conn->addr_cur = NULL; /* for safety */
if (conn->connhost[i].type == CHT_UNIX_SOCKET)
family = AF_UNIX;
#endif
pg_freeaddrinfo_all(family,
conn->connhost[i].addrlist);
conn->connhost[i].addrlist = NULL;
}
} }
conn->addr_cur = NULL;
} }
/* /*
...@@ -3723,7 +3706,7 @@ closePGconn(PGconn *conn) ...@@ -3723,7 +3706,7 @@ closePGconn(PGconn *conn)
conn->xactStatus = PQTRANS_IDLE; conn->xactStatus = PQTRANS_IDLE;
pqClearAsyncResult(conn); /* deallocate result */ pqClearAsyncResult(conn); /* deallocate result */
resetPQExpBuffer(&conn->errorMessage); resetPQExpBuffer(&conn->errorMessage);
release_all_addrinfo(conn); release_conn_addrinfo(conn);
/* Reset all state obtained from server, too */ /* Reset all state obtained from server, too */
pqDropServerData(conn); pqDropServerData(conn);
......
...@@ -312,7 +312,6 @@ typedef struct pg_conn_host ...@@ -312,7 +312,6 @@ typedef struct pg_conn_host
char *password; /* password for this host, read from the char *password; /* password for this host, read from the
* password file; NULL if not sought or not * password file; NULL if not sought or not
* found in password file. */ * found in password file. */
struct addrinfo *addrlist; /* list of possible backend addresses */
} pg_conn_host; } pg_conn_host;
/* /*
...@@ -412,7 +411,9 @@ struct pg_conn ...@@ -412,7 +411,9 @@ struct pg_conn
/* Transient state needed while establishing connection */ /* Transient state needed while establishing connection */
bool try_next_addr; /* time to advance to next address/host? */ bool try_next_addr; /* time to advance to next address/host? */
bool try_next_host; /* time to advance to next connhost[]? */ bool try_next_host; /* time to advance to next connhost[]? */
struct addrinfo *addr_cur; /* backend address currently being tried */ struct addrinfo *addrlist; /* list of addresses for current connhost */
struct addrinfo *addr_cur; /* the one currently being tried */
int addrlist_family; /* needed to know how to free addrlist */
PGSetenvStatusType setenv_state; /* for 2.0 protocol only */ PGSetenvStatusType setenv_state; /* for 2.0 protocol only */
const PQEnvironmentOption *next_eo; const PQEnvironmentOption *next_eo;
bool send_appname; /* okay to send application_name? */ bool send_appname; /* okay to send application_name? */
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment