<itemizedlist>
<listitem>
<para>
- The <literal>hostaddr</literal> and <literal>host</literal> parameters are used appropriately to ensure that
- name and reverse name queries are not made. See the documentation of
- these parameters in <xref linkend="libpq-paramkeywords"/> for details.
+ The <literal>hostaddr</literal> parameter must be used appropriately
+ to prevent DNS queries from being made. See the documentation of
+ this parameter in <xref linkend="libpq-paramkeywords"/> for details.
</para>
</listitem>
<listitem>
<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.
</para>
</listitem>
</para>
<para>
- Note: use of <function>PQconnectStartParams</function> is analogous to
- <function>PQconnectStart</function> shown below.
+ To begin a nonblocking connection request,
+ 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>
- To begin a nonblocking connection request, call <literal>conn = PQconnectStart("<replaceable>connection_info_string</replaceable>")</literal>.
- If <varname>conn</varname> 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). On return from
- <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.
+ If <function>PQconnectStart</function>
+ or <function>PQconnectStartParams</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
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
<symbol>PGRES_POLLING_READING</symbol>, wait until the socket is ready to
read (as indicated by <function>select()</function>, <function>poll()</function>, or
Conversely, if <function>PQconnectPoll(conn)</function> last returned
<symbol>PGRES_POLLING_WRITING</symbol>, wait until the socket is ready
to write, then call <function>PQconnectPoll(conn)</function> again.
- If you have yet to call
- <function>PQconnectPoll</function>, i.e., just after the call to
- <function>PQconnectStart</function>, behave as if it last returned
+ On the first iteration, i.e. if you have yet to call
+ <function>PQconnectPoll</function>, behave as if it last returned
<symbol>PGRES_POLLING_WRITING</symbol>. Continue this loop until
<function>PQconnectPoll(conn)</function> returns
<symbol>PGRES_POLLING_FAILED</symbol>, indicating the connection procedure
</para>
<para>
- Note that if <function>PQconnectStart</function> returns a non-null pointer, you must call
- <function>PQfinish</function> when you are 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.
+ Note that when <function>PQconnectStart</function>
+ or <function>PQconnectStartParams</function> returns a non-null
+ pointer, you must call <function>PQfinish</function> when you are
+ 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>
</listitem>
</varlistentry>
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>,
<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,
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
<para>
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
- format, a single host name can also translate to multiple network addresses. A
- common example of this is a host that has both an IPv4 and an IPv6 address.
+ separated by commas, in the <literal>host</literal> component of the URI.
+ </para>
+
+ <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>
Name of host to connect to.<indexterm><primary>host name</primary></indexterm>
If a host name begins with a slash, it specifies Unix-domain
communication rather than TCP/IP communication; the value is the
- name of the directory in which the socket file is stored. If
- multiple host names are specified, each will be tried in turn in
- the order given. The default behavior when <literal>host</literal> is
+ name of the directory in which the socket file is stored.
+ The default behavior when <literal>host</literal> is
not specified, or is empty, is to connect to a Unix-domain
socket<indexterm><primary>Unix domain socket</primary></indexterm> in
<filename>/tmp</filename> (or whatever socket directory was specified
<itemizedlist>
<listitem>
<para>
- If <literal>host</literal> is specified without <literal>hostaddr</literal>,
- a host name lookup occurs.
+ If <literal>host</literal> is specified
+ 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>
</listitem>
<listitem>
static bool fillPGconn(PGconn *conn, PQconninfoOption *connOptions);
static void freePGconn(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 PQconninfoOption *conninfo_init(PQExpBuffer errorMessage);
static PQconninfoOption *parse_connection_string(const char *conninfo,
static int
connectDBStart(PGconn *conn)
{
- char portstr[MAXPGPATH];
- int ret;
- int i;
-
if (!conn)
return 0;
*/
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
* a bit of a cheat, but PQconnectPoll will advance it to 0 before
/* Time to advance to next connhost[] entry? */
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)
{
/*
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;
+
+ /* Drop any address info for previous host */
+ 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;
+ }
+ 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;
conn->try_next_host = false;
}
return PGRES_POLLING_READING;
}
- /* We can release the address lists now. */
- release_all_addrinfo(conn);
+ /* We can release the address list now. */
+ release_conn_addrinfo(conn);
/* We are open for business! */
conn->status = CONNECTION_OK;
return PGRES_POLLING_READING;
}
- /* We can release the address lists now. */
- release_all_addrinfo(conn);
+ /* We can release the address list now. */
+ release_conn_addrinfo(conn);
/* We are open for business! */
conn->status = CONNECTION_OK;
goto keep_going;
}
+ /* We can release the address list now. */
+ release_conn_addrinfo(conn);
+
/* We are open for business! */
conn->status = CONNECTION_OK;
return PGRES_POLLING_OK;
PQclear(res);
termPQExpBuffer(&savedMessage);
- /* We can release the address lists now. */
- release_all_addrinfo(conn);
-
/*
* Finish reading any remaining messages before being
* considered as ready.
}
/*
- * release_all_addrinfo
- * - free addrinfo of all hostconn elements.
+ * release_conn_addrinfo
+ * - Free any addrinfo list in the PGconn.
*/
-
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;
-
-#ifdef HAVE_UNIX_SOCKETS
- 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;
- }
+ pg_freeaddrinfo_all(conn->addrlist_family, conn->addrlist);
+ conn->addrlist = NULL;
+ conn->addr_cur = NULL; /* for safety */
}
- conn->addr_cur = NULL;
}
/*
conn->xactStatus = PQTRANS_IDLE;
pqClearAsyncResult(conn); /* deallocate result */
resetPQExpBuffer(&conn->errorMessage);
- release_all_addrinfo(conn);
+ release_conn_addrinfo(conn);
/* Reset all state obtained from server, too */
pqDropServerData(conn);