Commit 274bb2b3 authored by Robert Haas's avatar Robert Haas

libpq: Allow connection strings and URIs to specify multiple hosts.

It's also possible to specify a separate port for each host.

Previously, we'd loop over every address returned by looking up the
host name; now, we'll try every address for every host name.

Patch by me.  Victor Wagner wrote an earlier patch for this feature,
which I read, but I didn't use any of his code.  Review by Mithun Cy.
parent 77067106
......@@ -756,8 +756,10 @@ PGPing PQping(const char *conninfo);
Several <application>libpq</> functions parse a user-specified string to obtain
connection parameters. There are two accepted formats for these strings:
plain <literal>keyword = value</literal> strings
and <ulink url="http://www.ietf.org/rfc/rfc3986.txt">RFC
3986</ulink> URIs.
and URIs. URIs generally follow
<ulink url="http://www.ietf.org/rfc/rfc3986.txt">RFC
3986</ulink>, except that multi-host connection strings are allowed
as further described below.
</para>
<sect3>
......@@ -792,7 +794,7 @@ host=localhost port=5432 dbname=mydb connect_timeout=10
<para>
The general form for a connection <acronym>URI</acronym> is:
<synopsis>
postgresql://[user[:password]@][netloc][:port][/dbname][?param1=value1&amp;...]
postgresql://[user[:password]@][netloc][:port][,...][/dbname][?param1=value1&amp;...]
</synopsis>
</para>
......@@ -809,6 +811,7 @@ postgresql://localhost/mydb
postgresql://user@localhost
postgresql://user:secret@localhost
postgresql://other@localhost/otherdb?connect_timeout=10&amp;application_name=myapp
postgresql://host1:123,host2:456/somedb
</programlisting>
Components of the hierarchical part of the <acronym>URI</acronym> can also
be given as parameters. For example:
......@@ -856,6 +859,15 @@ postgresql:///dbname?host=/var/lib/postgresql
postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
</programlisting>
</para>
<para>
It is possible to specify multiple host components, each with an optional
port component, in a single URI. A URI of the form
<literal>postgresql://host1:port1,host2:port2,host3:port3/</literal>
is equivalent to a connection string of the form
<literal>host=host1,host2,host3 port=port1,port2,port3</literal>. Each
host will be tried in turn until a connection is successfully established.
</para>
</sect3>
</sect2>
......@@ -870,12 +882,13 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
<term><literal>host</literal></term>
<listitem>
<para>
Name of host to connect to.<indexterm><primary>host name</></>
If this begins with a slash, it specifies Unix-domain
Comma-separated list of host names.<indexterm><primary>host name</></>
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. The
default behavior when <literal>host</literal> is not specified
is to connect to a Unix-domain
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
not specified is to connect to a Unix-domain
socket<indexterm><primary>Unix domain socket</></> in
<filename>/tmp</filename> (or whatever socket directory was specified
when <productname>PostgreSQL</> was built). On machines without
......@@ -950,6 +963,9 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
Port number to connect to at the server host, or socket file
name extension for Unix-domain
connections.<indexterm><primary>port</></>
If the <literal>host</> parameter included multiple, comma-separated
hosts, this parameter may specify a list of ports of equal length,
or it may specify a single port number to be used for all hosts.
</para>
</listitem>
</varlistentry>
......@@ -1394,7 +1410,11 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
<para>
The following functions return parameter values established at connection.
These values are fixed for the life of the <structname>PGconn</> object.
These values are fixed for the life of the connection. If a multi-host
connection string is used, the values of <function>PQhost</>,
<function>PQport</>, and <function>PQpass</> can change if a new connection
is established using the same <structname>PGconn</> object. Other values
are fixed for the lifetime of the <structname>PGconn</> object.
<variablelist>
<varlistentry id="libpq-pqdb">
......
......@@ -683,20 +683,26 @@ pg_fe_sendauth(AuthRequest areq, PGconn *conn)
case AUTH_REQ_MD5:
case AUTH_REQ_PASSWORD:
conn->password_needed = true;
if (conn->pgpass == NULL || conn->pgpass[0] == '\0')
{
printfPQExpBuffer(&conn->errorMessage,
PQnoPasswordSupplied);
return STATUS_ERROR;
}
if (pg_password_sendauth(conn, conn->pgpass, areq) != STATUS_OK)
{
printfPQExpBuffer(&conn->errorMessage,
char *password = conn->connhost[conn->whichhost].password;
if (password == NULL)
password = conn->pgpass;
conn->password_needed = true;
if (password == NULL || password[0] == '\0')
{
printfPQExpBuffer(&conn->errorMessage,
PQnoPasswordSupplied);
return STATUS_ERROR;
}
if (pg_password_sendauth(conn, password, areq) != STATUS_OK)
{
printfPQExpBuffer(&conn->errorMessage,
"fe_sendauth: error sending password authentication\n");
return STATUS_ERROR;
return STATUS_ERROR;
}
break;
}
break;
case AUTH_REQ_SCM_CREDS:
if (pg_local_sendauth(conn) != STATUS_OK)
......
This diff is collapsed.
......@@ -292,6 +292,30 @@ typedef struct pgDataValue
const char *value; /* data value, without zero-termination */
} PGdataValue;
typedef enum pg_conn_host_type
{
CHT_HOST_NAME,
CHT_HOST_ADDRESS,
CHT_UNIX_SOCKET
} 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.
*/
typedef struct pg_conn_host
{
char *host; /* host name or address, or socket path */
pg_conn_host_type type; /* type of host */
char *port; /* port number for this host; if not NULL,
* overrrides the PGConn's pgport */
char *password; /* password for this host, read from the
* password file. only set if the PGconn's
* pgpass field is NULL. */
struct addrinfo *addrlist; /* list of possible backend addresses */
} pg_conn_host;
/*
* PGconn stores all the state data associated with a single connection
* to a backend.
......@@ -299,13 +323,15 @@ typedef struct pgDataValue
struct pg_conn
{
/* Saved values of connection options */
char *pghost; /* the machine on which the server is running */
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 */
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 */
char *pgunixsocket; /* the directory of the server's Unix-domain
* socket; if NULL, use DEFAULT_PGSOCKET_DIR */
char *pgtty; /* tty on which the backend messages is
* displayed (OBSOLETE, NOT USED) */
char *connect_timeout; /* connection timeout (numeric string) */
......@@ -363,6 +389,11 @@ struct pg_conn
PGnotify *notifyHead; /* oldest unreported Notify msg */
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 */
/* Connection data */
pgsocket sock; /* FD for socket, PGINVALID_SOCKET if
* unconnected */
......@@ -378,9 +409,7 @@ struct pg_conn
bool sigpipe_flag; /* can we mask SIGPIPE via MSG_NOSIGNAL? */
/* Transient state needed while establishing connection */
struct addrinfo *addrlist; /* list of possible backend addresses */
struct addrinfo *addr_cur; /* the one currently being tried */
int addrlist_family; /* needed to know how to free addrlist */
struct addrinfo *addr_cur; /* backend address currently being tried */
PGSetenvStatusType setenv_state; /* for 2.0 protocol only */
const PQEnvironmentOption *next_eo;
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