mirror of
https://github.com/postgres/postgres.git
synced 2025-04-27 22:56:53 +03:00
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.
This commit is contained in:
parent
770671062f
commit
274bb2b385
@ -756,8 +756,10 @@ PGPing PQping(const char *conninfo);
|
|||||||
Several <application>libpq</> functions parse a user-specified string to obtain
|
Several <application>libpq</> functions parse a user-specified string to obtain
|
||||||
connection parameters. There are two accepted formats for these strings:
|
connection parameters. There are two accepted formats for these strings:
|
||||||
plain <literal>keyword = value</literal> strings
|
plain <literal>keyword = value</literal> strings
|
||||||
and <ulink url="http://www.ietf.org/rfc/rfc3986.txt">RFC
|
and URIs. URIs generally follow
|
||||||
3986</ulink> URIs.
|
<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>
|
</para>
|
||||||
|
|
||||||
<sect3>
|
<sect3>
|
||||||
@ -792,7 +794,7 @@ host=localhost port=5432 dbname=mydb connect_timeout=10
|
|||||||
<para>
|
<para>
|
||||||
The general form for a connection <acronym>URI</acronym> is:
|
The general form for a connection <acronym>URI</acronym> is:
|
||||||
<synopsis>
|
<synopsis>
|
||||||
postgresql://[user[:password]@][netloc][:port][/dbname][?param1=value1&...]
|
postgresql://[user[:password]@][netloc][:port][,...][/dbname][?param1=value1&...]
|
||||||
</synopsis>
|
</synopsis>
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
@ -809,6 +811,7 @@ postgresql://localhost/mydb
|
|||||||
postgresql://user@localhost
|
postgresql://user@localhost
|
||||||
postgresql://user:secret@localhost
|
postgresql://user:secret@localhost
|
||||||
postgresql://other@localhost/otherdb?connect_timeout=10&application_name=myapp
|
postgresql://other@localhost/otherdb?connect_timeout=10&application_name=myapp
|
||||||
|
postgresql://host1:123,host2:456/somedb
|
||||||
</programlisting>
|
</programlisting>
|
||||||
Components of the hierarchical part of the <acronym>URI</acronym> can also
|
Components of the hierarchical part of the <acronym>URI</acronym> can also
|
||||||
be given as parameters. For example:
|
be given as parameters. For example:
|
||||||
@ -856,6 +859,15 @@ postgresql:///dbname?host=/var/lib/postgresql
|
|||||||
postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
|
postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
|
||||||
</programlisting>
|
</programlisting>
|
||||||
</para>
|
</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>
|
</sect3>
|
||||||
</sect2>
|
</sect2>
|
||||||
|
|
||||||
@ -870,12 +882,13 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
|
|||||||
<term><literal>host</literal></term>
|
<term><literal>host</literal></term>
|
||||||
<listitem>
|
<listitem>
|
||||||
<para>
|
<para>
|
||||||
Name of host to connect to.<indexterm><primary>host name</></>
|
Comma-separated list of host names.<indexterm><primary>host name</></>
|
||||||
If this 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. The
|
name of the directory in which the socket file is stored. If
|
||||||
default behavior when <literal>host</literal> is not specified
|
multiple host names are specified, each will be tried in turn in
|
||||||
is to connect to a Unix-domain
|
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
|
socket<indexterm><primary>Unix domain socket</></> in
|
||||||
<filename>/tmp</filename> (or whatever socket directory was specified
|
<filename>/tmp</filename> (or whatever socket directory was specified
|
||||||
when <productname>PostgreSQL</> was built). On machines without
|
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
|
Port number to connect to at the server host, or socket file
|
||||||
name extension for Unix-domain
|
name extension for Unix-domain
|
||||||
connections.<indexterm><primary>port</></>
|
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>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
@ -1394,7 +1410,11 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
|
|||||||
|
|
||||||
<para>
|
<para>
|
||||||
The following functions return parameter values established at connection.
|
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>
|
<variablelist>
|
||||||
<varlistentry id="libpq-pqdb">
|
<varlistentry id="libpq-pqdb">
|
||||||
|
@ -683,20 +683,26 @@ pg_fe_sendauth(AuthRequest areq, PGconn *conn)
|
|||||||
|
|
||||||
case AUTH_REQ_MD5:
|
case AUTH_REQ_MD5:
|
||||||
case AUTH_REQ_PASSWORD:
|
case AUTH_REQ_PASSWORD:
|
||||||
conn->password_needed = true;
|
|
||||||
if (conn->pgpass == NULL || conn->pgpass[0] == '\0')
|
|
||||||
{
|
{
|
||||||
printfPQExpBuffer(&conn->errorMessage,
|
char *password = conn->connhost[conn->whichhost].password;
|
||||||
PQnoPasswordSupplied);
|
|
||||||
return STATUS_ERROR;
|
if (password == NULL)
|
||||||
}
|
password = conn->pgpass;
|
||||||
if (pg_password_sendauth(conn, conn->pgpass, areq) != STATUS_OK)
|
conn->password_needed = true;
|
||||||
{
|
if (password == NULL || password[0] == '\0')
|
||||||
printfPQExpBuffer(&conn->errorMessage,
|
{
|
||||||
|
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");
|
"fe_sendauth: error sending password authentication\n");
|
||||||
return STATUS_ERROR;
|
return STATUS_ERROR;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
|
|
||||||
case AUTH_REQ_SCM_CREDS:
|
case AUTH_REQ_SCM_CREDS:
|
||||||
if (pg_local_sendauth(conn) != STATUS_OK)
|
if (pg_local_sendauth(conn) != STATUS_OK)
|
||||||
|
@ -770,6 +770,148 @@ connectOptions1(PGconn *conn, const char *conninfo)
|
|||||||
static bool
|
static bool
|
||||||
connectOptions2(PGconn *conn)
|
connectOptions2(PGconn *conn)
|
||||||
{
|
{
|
||||||
|
/*
|
||||||
|
* Allocate memory for details about each host to which we might possibly
|
||||||
|
* try to connect. If pghostaddr is set, we're only going to try to
|
||||||
|
* connect to that one particular address. If it's not, we'll use pghost,
|
||||||
|
* which may contain multiple, comma-separated names.
|
||||||
|
*/
|
||||||
|
conn->nconnhost = 1;
|
||||||
|
conn->whichhost = 0;
|
||||||
|
if ((conn->pghostaddr == NULL || conn->pghostaddr[0] == '\0')
|
||||||
|
&& conn->pghost != NULL)
|
||||||
|
{
|
||||||
|
char *s;
|
||||||
|
|
||||||
|
for (s = conn->pghost; *s != '\0'; ++s)
|
||||||
|
if (*s == ',')
|
||||||
|
conn->nconnhost++;
|
||||||
|
}
|
||||||
|
conn->connhost = (pg_conn_host *)
|
||||||
|
calloc(conn->nconnhost, sizeof(pg_conn_host));
|
||||||
|
if (conn->connhost == NULL)
|
||||||
|
goto oom_error;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We now have one pg_conn_host structure per possible host. Fill in
|
||||||
|
* the host details for each one.
|
||||||
|
*/
|
||||||
|
if (conn->pghostaddr != NULL && conn->pghostaddr[0] != '\0')
|
||||||
|
{
|
||||||
|
conn->connhost[0].host = strdup(conn->pghostaddr);
|
||||||
|
if (conn->connhost[0].host == NULL)
|
||||||
|
goto oom_error;
|
||||||
|
conn->connhost[0].type = CHT_HOST_ADDRESS;
|
||||||
|
}
|
||||||
|
else if (conn->pghost != NULL && conn->pghost[0] != '\0')
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
char *s = conn->pghost;
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
char *e = s;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Search for the end of the current hostname; a comma or
|
||||||
|
* end-of-string acts as a terminator.
|
||||||
|
*/
|
||||||
|
while (*e != '\0' && *e != ',')
|
||||||
|
++e;
|
||||||
|
|
||||||
|
/* Copy the hostname whose bounds we just identified. */
|
||||||
|
conn->connhost[i].host =
|
||||||
|
(char *) malloc(sizeof(char) * (e - s + 1));
|
||||||
|
if (conn->connhost[i].host == NULL)
|
||||||
|
goto oom_error;
|
||||||
|
memcpy(conn->connhost[i].host, s, e - s);
|
||||||
|
conn->connhost[i].host[e - s] = '\0';
|
||||||
|
|
||||||
|
/* Identify the type of host. */
|
||||||
|
conn->connhost[i].type = CHT_HOST_NAME;
|
||||||
|
#ifdef HAVE_UNIX_SOCKETS
|
||||||
|
if (is_absolute_path(conn->connhost[i].host))
|
||||||
|
conn->connhost[i].type = CHT_UNIX_SOCKET;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Prepare to find the next host (if any). */
|
||||||
|
if (*e == '\0')
|
||||||
|
break;
|
||||||
|
s = e + 1;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
#ifdef HAVE_UNIX_SOCKETS
|
||||||
|
conn->connhost[0].host = strdup(DEFAULT_PGSOCKET_DIR);
|
||||||
|
conn->connhost[0].type = CHT_UNIX_SOCKET;
|
||||||
|
#else
|
||||||
|
conn->connhost[0].host = strdup(DefaultHost);
|
||||||
|
conn->connhost[0].type = CHT_HOST_NAME;
|
||||||
|
#endif
|
||||||
|
if (conn->connhost[0].host == NULL)
|
||||||
|
goto oom_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Next, work out the port number corresponding to each host name.
|
||||||
|
*/
|
||||||
|
if (conn->pgport != NULL && conn->pgport[0] != '\0')
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
char *s = conn->pgport;
|
||||||
|
int nports = 1;
|
||||||
|
|
||||||
|
for (i = 0; i < conn->nconnhost; ++i)
|
||||||
|
{
|
||||||
|
char *e = s;
|
||||||
|
|
||||||
|
/* Search for the end of the current port number. */
|
||||||
|
while (*e != '\0' && *e != ',')
|
||||||
|
++e;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we found a port number of non-zero length, copy it.
|
||||||
|
* Otherwise, insert the default port number.
|
||||||
|
*/
|
||||||
|
if (e > s)
|
||||||
|
{
|
||||||
|
conn->connhost[i].port =
|
||||||
|
(char *) malloc(sizeof(char) * (e - s + 1));
|
||||||
|
if (conn->connhost[i].port == NULL)
|
||||||
|
goto oom_error;
|
||||||
|
memcpy(conn->connhost[i].port, s, e - s);
|
||||||
|
conn->connhost[i].port[e - s] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Move on to the next port number, unless there are no more.
|
||||||
|
* (If only one part number is specified, we reuse it for every
|
||||||
|
* host.)
|
||||||
|
*/
|
||||||
|
if (*e != '\0')
|
||||||
|
{
|
||||||
|
s = e + 1;
|
||||||
|
++nports;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If multiple ports were specified, there must be exactly as many
|
||||||
|
* ports as there were hosts. Otherwise, we do not know how to match
|
||||||
|
* them up.
|
||||||
|
*/
|
||||||
|
if (nports != 1 && nports != conn->nconnhost)
|
||||||
|
{
|
||||||
|
conn->status = CONNECTION_BAD;
|
||||||
|
printfPQExpBuffer(&conn->errorMessage,
|
||||||
|
libpq_gettext("could not match %d port numbers to %d hosts\n"),
|
||||||
|
nports, conn->nconnhost);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If user name was not given, fetch it. (Most likely, the fetch will
|
* If user name was not given, fetch it. (Most likely, the fetch will
|
||||||
* fail, since the only way we get here is if pg_fe_getauthname() failed
|
* fail, since the only way we get here is if pg_fe_getauthname() failed
|
||||||
@ -800,33 +942,27 @@ connectOptions2(PGconn *conn)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Supply default password if none given
|
* Supply default password if none given. Note that the password might
|
||||||
|
* be different for each host/port pair.
|
||||||
*/
|
*/
|
||||||
if (conn->pgpass == NULL || conn->pgpass[0] == '\0')
|
if (conn->pgpass == NULL || conn->pgpass[0] == '\0')
|
||||||
{
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
if (conn->pgpass)
|
if (conn->pgpass)
|
||||||
free(conn->pgpass);
|
free(conn->pgpass);
|
||||||
conn->pgpass = PasswordFromFile(conn->pghost, conn->pgport,
|
conn->pgpass = strdup(DefaultPassword);
|
||||||
conn->dbName, conn->pguser);
|
if (!conn->pgpass)
|
||||||
if (conn->pgpass == NULL)
|
goto oom_error;
|
||||||
|
for (i = 0; i < conn->nconnhost; ++i)
|
||||||
{
|
{
|
||||||
conn->pgpass = strdup(DefaultPassword);
|
conn->connhost[i].password =
|
||||||
if (!conn->pgpass)
|
PasswordFromFile(conn->connhost[i].host,
|
||||||
goto oom_error;
|
conn->connhost[i].port,
|
||||||
|
conn->dbName, conn->pguser);
|
||||||
|
if (conn->connhost[i].password != NULL)
|
||||||
|
conn->dot_pgpass_used = true;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
conn->dot_pgpass_used = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Allow unix socket specification in the host name
|
|
||||||
*/
|
|
||||||
if (conn->pghost && is_absolute_path(conn->pghost))
|
|
||||||
{
|
|
||||||
if (conn->pgunixsocket)
|
|
||||||
free(conn->pgunixsocket);
|
|
||||||
conn->pgunixsocket = conn->pghost;
|
|
||||||
conn->pghost = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1142,6 +1278,7 @@ connectFailureMessage(PGconn *conn, int errorno)
|
|||||||
{
|
{
|
||||||
char host_addr[NI_MAXHOST];
|
char host_addr[NI_MAXHOST];
|
||||||
const char *displayed_host;
|
const char *displayed_host;
|
||||||
|
const char *displayed_port;
|
||||||
struct sockaddr_storage *addr = &conn->raddr.addr;
|
struct sockaddr_storage *addr = &conn->raddr.addr;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1171,12 +1308,11 @@ connectFailureMessage(PGconn *conn, int errorno)
|
|||||||
else
|
else
|
||||||
strcpy(host_addr, "???");
|
strcpy(host_addr, "???");
|
||||||
|
|
||||||
if (conn->pghostaddr && conn->pghostaddr[0] != '\0')
|
/* To which host and port were we actually connecting? */
|
||||||
displayed_host = conn->pghostaddr;
|
displayed_host = conn->connhost[conn->whichhost].host;
|
||||||
else if (conn->pghost && conn->pghost[0] != '\0')
|
displayed_port = conn->connhost[conn->whichhost].port;
|
||||||
displayed_host = conn->pghost;
|
if (displayed_port == NULL || displayed_port[0] == '\0')
|
||||||
else
|
displayed_port = DEF_PGPORT_STR;
|
||||||
displayed_host = DefaultHost;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the user did not supply an IP address using 'hostaddr', and
|
* If the user did not supply an IP address using 'hostaddr', and
|
||||||
@ -1192,7 +1328,7 @@ connectFailureMessage(PGconn *conn, int errorno)
|
|||||||
SOCK_STRERROR(errorno, sebuf, sizeof(sebuf)),
|
SOCK_STRERROR(errorno, sebuf, sizeof(sebuf)),
|
||||||
displayed_host,
|
displayed_host,
|
||||||
host_addr,
|
host_addr,
|
||||||
conn->pgport);
|
displayed_port);
|
||||||
else
|
else
|
||||||
appendPQExpBuffer(&conn->errorMessage,
|
appendPQExpBuffer(&conn->errorMessage,
|
||||||
libpq_gettext("could not connect to server: %s\n"
|
libpq_gettext("could not connect to server: %s\n"
|
||||||
@ -1200,7 +1336,7 @@ connectFailureMessage(PGconn *conn, int errorno)
|
|||||||
"\tTCP/IP connections on port %s?\n"),
|
"\tTCP/IP connections on port %s?\n"),
|
||||||
SOCK_STRERROR(errorno, sebuf, sizeof(sebuf)),
|
SOCK_STRERROR(errorno, sebuf, sizeof(sebuf)),
|
||||||
displayed_host,
|
displayed_host,
|
||||||
conn->pgport);
|
displayed_port);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1390,12 +1526,9 @@ setKeepalivesWin32(PGconn *conn)
|
|||||||
static int
|
static int
|
||||||
connectDBStart(PGconn *conn)
|
connectDBStart(PGconn *conn)
|
||||||
{
|
{
|
||||||
int portnum;
|
|
||||||
char portstr[MAXPGPATH];
|
char portstr[MAXPGPATH];
|
||||||
struct addrinfo *addrs = NULL;
|
|
||||||
struct addrinfo hint;
|
|
||||||
const char *node;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
int i;
|
||||||
|
|
||||||
if (!conn)
|
if (!conn)
|
||||||
return 0;
|
return 0;
|
||||||
@ -1408,83 +1541,86 @@ connectDBStart(PGconn *conn)
|
|||||||
conn->outCount = 0;
|
conn->outCount = 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Determine the parameters to pass to pg_getaddrinfo_all.
|
* Look up socket addresses for each possible host using
|
||||||
|
* pg_getaddrinfo_all.
|
||||||
*/
|
*/
|
||||||
|
for (i = 0; i < conn->nconnhost; ++i)
|
||||||
/* Initialize hint structure */
|
|
||||||
MemSet(&hint, 0, sizeof(hint));
|
|
||||||
hint.ai_socktype = SOCK_STREAM;
|
|
||||||
hint.ai_family = AF_UNSPEC;
|
|
||||||
|
|
||||||
/* Set up port number as a string */
|
|
||||||
if (conn->pgport != NULL && conn->pgport[0] != '\0')
|
|
||||||
{
|
{
|
||||||
portnum = atoi(conn->pgport);
|
pg_conn_host *ch = &conn->connhost[i];
|
||||||
if (portnum < 1 || portnum > 65535)
|
char *node = ch->host;
|
||||||
{
|
struct addrinfo hint;
|
||||||
appendPQExpBuffer(&conn->errorMessage,
|
int thisport;
|
||||||
libpq_gettext("invalid port number: \"%s\"\n"),
|
|
||||||
conn->pgport);
|
|
||||||
conn->options_valid = false;
|
|
||||||
goto connect_errReturn;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
portnum = DEF_PGPORT;
|
|
||||||
snprintf(portstr, sizeof(portstr), "%d", portnum);
|
|
||||||
|
|
||||||
if (conn->pghostaddr != NULL && conn->pghostaddr[0] != '\0')
|
/* Initialize hint structure */
|
||||||
{
|
MemSet(&hint, 0, sizeof(hint));
|
||||||
/* Using pghostaddr avoids a hostname lookup */
|
hint.ai_socktype = SOCK_STREAM;
|
||||||
node = conn->pghostaddr;
|
|
||||||
hint.ai_family = AF_UNSPEC;
|
hint.ai_family = AF_UNSPEC;
|
||||||
hint.ai_flags = AI_NUMERICHOST;
|
|
||||||
}
|
|
||||||
else if (conn->pghost != NULL && conn->pghost[0] != '\0')
|
|
||||||
{
|
|
||||||
/* Using pghost, so we have to look-up the hostname */
|
|
||||||
node = conn->pghost;
|
|
||||||
hint.ai_family = AF_UNSPEC;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
#ifdef HAVE_UNIX_SOCKETS
|
|
||||||
/* pghostaddr and pghost are NULL, so use Unix domain socket */
|
|
||||||
node = NULL;
|
|
||||||
hint.ai_family = AF_UNIX;
|
|
||||||
UNIXSOCK_PATH(portstr, portnum, conn->pgunixsocket);
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
/* Without Unix sockets, default to localhost instead */
|
|
||||||
node = DefaultHost;
|
|
||||||
hint.ai_family = AF_UNSPEC;
|
|
||||||
#endif /* HAVE_UNIX_SOCKETS */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Use pg_getaddrinfo_all() to resolve the address */
|
/* Figure out the port number we're going to use. */
|
||||||
ret = pg_getaddrinfo_all(node, portstr, &hint, &addrs);
|
if (ch->port == NULL)
|
||||||
if (ret || !addrs)
|
thisport = DEF_PGPORT;
|
||||||
{
|
|
||||||
if (node)
|
|
||||||
appendPQExpBuffer(&conn->errorMessage,
|
|
||||||
libpq_gettext("could not translate host name \"%s\" to address: %s\n"),
|
|
||||||
node, gai_strerror(ret));
|
|
||||||
else
|
else
|
||||||
appendPQExpBuffer(&conn->errorMessage,
|
{
|
||||||
libpq_gettext("could not translate Unix-domain socket path \"%s\" to address: %s\n"),
|
thisport = atoi(ch->port);
|
||||||
portstr, gai_strerror(ret));
|
if (thisport < 1 || thisport > 65535)
|
||||||
if (addrs)
|
{
|
||||||
pg_freeaddrinfo_all(hint.ai_family, addrs);
|
appendPQExpBuffer(&conn->errorMessage,
|
||||||
conn->options_valid = false;
|
libpq_gettext("invalid port number: \"%s\"\n"),
|
||||||
goto connect_errReturn;
|
ch->port);
|
||||||
|
conn->options_valid = false;
|
||||||
|
goto connect_errReturn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
snprintf(portstr, sizeof(portstr), "%d", thisport);
|
||||||
|
|
||||||
|
/* Set up for name resolution. */
|
||||||
|
switch (ch->type)
|
||||||
|
{
|
||||||
|
case CHT_HOST_NAME:
|
||||||
|
break;
|
||||||
|
case CHT_HOST_ADDRESS:
|
||||||
|
hint.ai_flags = AI_NUMERICHOST;
|
||||||
|
break;
|
||||||
|
case CHT_UNIX_SOCKET:
|
||||||
|
#ifdef HAVE_UNIX_SOCKETS
|
||||||
|
node = NULL;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
Assert(false);
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Use pg_getaddrinfo_all() to resolve the address */
|
||||||
|
ret = pg_getaddrinfo_all(node, portstr, &hint, &ch->addrlist);
|
||||||
|
if (ret || !ch->addrlist)
|
||||||
|
{
|
||||||
|
if (node)
|
||||||
|
appendPQExpBuffer(&conn->errorMessage,
|
||||||
|
libpq_gettext("could not translate host name \"%s\" to address: %s\n"),
|
||||||
|
node, gai_strerror(ret));
|
||||||
|
else
|
||||||
|
appendPQExpBuffer(&conn->errorMessage,
|
||||||
|
libpq_gettext("could not translate Unix-domain socket path \"%s\" to address: %s\n"),
|
||||||
|
portstr, gai_strerror(ret));
|
||||||
|
if (ch->addrlist)
|
||||||
|
{
|
||||||
|
pg_freeaddrinfo_all(hint.ai_family, ch->addrlist);
|
||||||
|
ch->addrlist = NULL;
|
||||||
|
}
|
||||||
|
conn->options_valid = false;
|
||||||
|
goto connect_errReturn;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef USE_SSL
|
#ifdef USE_SSL
|
||||||
@ -1498,9 +1634,8 @@ connectDBStart(PGconn *conn)
|
|||||||
/*
|
/*
|
||||||
* Set up to try to connect, with protocol 3.0 as the first attempt.
|
* Set up to try to connect, with protocol 3.0 as the first attempt.
|
||||||
*/
|
*/
|
||||||
conn->addrlist = addrs;
|
conn->whichhost = 0;
|
||||||
conn->addr_cur = addrs;
|
conn->addr_cur = conn->connhost[0].addrlist;
|
||||||
conn->addrlist_family = hint.ai_family;
|
|
||||||
conn->pversion = PG_PROTOCOL(3, 0);
|
conn->pversion = PG_PROTOCOL(3, 0);
|
||||||
conn->send_appname = true;
|
conn->send_appname = true;
|
||||||
conn->status = CONNECTION_NEEDED;
|
conn->status = CONNECTION_NEEDED;
|
||||||
@ -1702,11 +1837,27 @@ keep_going: /* We will come back to here until there is
|
|||||||
* returned by pg_getaddrinfo_all(). conn->addr_cur is the
|
* 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. We fail when we run out of addresses.
|
||||||
*/
|
*/
|
||||||
while (conn->addr_cur != NULL)
|
for (;;)
|
||||||
{
|
{
|
||||||
struct addrinfo *addr_cur = conn->addr_cur;
|
struct addrinfo *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 (++conn->whichhost >= conn->nconnhost)
|
||||||
|
{
|
||||||
|
conn->whichhost = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
conn->addr_cur =
|
||||||
|
conn->connhost[conn->whichhost].addrlist;
|
||||||
|
}
|
||||||
|
|
||||||
/* Remember current address for possible error msg */
|
/* Remember current address for possible error msg */
|
||||||
|
addr_cur = conn->addr_cur;
|
||||||
memcpy(&conn->raddr.addr, addr_cur->ai_addr,
|
memcpy(&conn->raddr.addr, addr_cur->ai_addr,
|
||||||
addr_cur->ai_addrlen);
|
addr_cur->ai_addrlen);
|
||||||
conn->raddr.salen = addr_cur->ai_addrlen;
|
conn->raddr.salen = addr_cur->ai_addrlen;
|
||||||
@ -1718,7 +1869,8 @@ keep_going: /* We will come back to here until there is
|
|||||||
* ignore socket() failure if we have more addresses
|
* ignore socket() failure if we have more addresses
|
||||||
* to try
|
* to try
|
||||||
*/
|
*/
|
||||||
if (addr_cur->ai_next != NULL)
|
if (addr_cur->ai_next != NULL ||
|
||||||
|
conn->whichhost + 1 < conn->nconnhost)
|
||||||
{
|
{
|
||||||
conn->addr_cur = addr_cur->ai_next;
|
conn->addr_cur = addr_cur->ai_next;
|
||||||
continue;
|
continue;
|
||||||
@ -1944,7 +2096,8 @@ keep_going: /* We will come back to here until there is
|
|||||||
* If more addresses remain, keep trying, just as in the
|
* If more addresses remain, keep trying, just as in the
|
||||||
* case where connect() returned failure immediately.
|
* case where connect() returned failure immediately.
|
||||||
*/
|
*/
|
||||||
if (conn->addr_cur->ai_next != NULL)
|
if (conn->addr_cur->ai_next != NULL ||
|
||||||
|
conn->whichhost + 1 < conn->nconnhost)
|
||||||
{
|
{
|
||||||
conn->addr_cur = conn->addr_cur->ai_next;
|
conn->addr_cur = conn->addr_cur->ai_next;
|
||||||
conn->status = CONNECTION_NEEDED;
|
conn->status = CONNECTION_NEEDED;
|
||||||
@ -2599,9 +2752,25 @@ keep_going: /* We will come back to here until there is
|
|||||||
goto error_return;
|
goto error_return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* We can release the address list now. */
|
/* We can release the address lists now. */
|
||||||
pg_freeaddrinfo_all(conn->addrlist_family, conn->addrlist);
|
if (conn->connhost != NULL)
|
||||||
conn->addrlist = NULL;
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
conn->addr_cur = NULL;
|
conn->addr_cur = NULL;
|
||||||
|
|
||||||
/* Fire up post-connection housekeeping if needed */
|
/* Fire up post-connection housekeeping if needed */
|
||||||
@ -2858,6 +3027,21 @@ freePGconn(PGconn *conn)
|
|||||||
free(conn->events[i].name);
|
free(conn->events[i].name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* clean up pg_conn_host structures */
|
||||||
|
if (conn->connhost != NULL)
|
||||||
|
{
|
||||||
|
for (i = 0; i < conn->nconnhost; ++i)
|
||||||
|
{
|
||||||
|
if (conn->connhost[i].host != NULL)
|
||||||
|
free(conn->connhost[i].host);
|
||||||
|
if (conn->connhost[i].port != NULL)
|
||||||
|
free(conn->connhost[i].port);
|
||||||
|
if (conn->connhost[i].password != NULL)
|
||||||
|
free(conn->connhost[i].password);
|
||||||
|
}
|
||||||
|
free(conn->connhost);
|
||||||
|
}
|
||||||
|
|
||||||
if (conn->client_encoding_initial)
|
if (conn->client_encoding_initial)
|
||||||
free(conn->client_encoding_initial);
|
free(conn->client_encoding_initial);
|
||||||
if (conn->events)
|
if (conn->events)
|
||||||
@ -2868,8 +3052,6 @@ freePGconn(PGconn *conn)
|
|||||||
free(conn->pghostaddr);
|
free(conn->pghostaddr);
|
||||||
if (conn->pgport)
|
if (conn->pgport)
|
||||||
free(conn->pgport);
|
free(conn->pgport);
|
||||||
if (conn->pgunixsocket)
|
|
||||||
free(conn->pgunixsocket);
|
|
||||||
if (conn->pgtty)
|
if (conn->pgtty)
|
||||||
free(conn->pgtty);
|
free(conn->pgtty);
|
||||||
if (conn->connect_timeout)
|
if (conn->connect_timeout)
|
||||||
@ -2983,8 +3165,24 @@ closePGconn(PGconn *conn)
|
|||||||
conn->asyncStatus = PGASYNC_IDLE;
|
conn->asyncStatus = PGASYNC_IDLE;
|
||||||
pqClearAsyncResult(conn); /* deallocate result */
|
pqClearAsyncResult(conn); /* deallocate result */
|
||||||
resetPQExpBuffer(&conn->errorMessage);
|
resetPQExpBuffer(&conn->errorMessage);
|
||||||
pg_freeaddrinfo_all(conn->addrlist_family, conn->addrlist);
|
if (conn->connhost != NULL)
|
||||||
conn->addrlist = NULL;
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
conn->addr_cur = NULL;
|
conn->addr_cur = NULL;
|
||||||
notify = conn->notifyHead;
|
notify = conn->notifyHead;
|
||||||
while (notify != NULL)
|
while (notify != NULL)
|
||||||
@ -4720,7 +4918,10 @@ conninfo_uri_parse(const char *uri, PQExpBuffer errorMessage,
|
|||||||
* postgresql://[user[:password]@][netloc][:port][/dbname][?param1=value1&...]
|
* postgresql://[user[:password]@][netloc][:port][/dbname][?param1=value1&...]
|
||||||
*
|
*
|
||||||
* where "netloc" is a hostname, an IPv4 address, or an IPv6 address surrounded
|
* where "netloc" is a hostname, an IPv4 address, or an IPv6 address surrounded
|
||||||
* by literal square brackets.
|
* by literal square brackets. As an extension, we also allow multiple
|
||||||
|
* netloc[:port] specifications, separated by commas:
|
||||||
|
*
|
||||||
|
* postgresql://[user[:password]@][netloc][:port][,...][/dbname][?param1=value1&...]
|
||||||
*
|
*
|
||||||
* Any of the URI parts might use percent-encoding (%xy).
|
* Any of the URI parts might use percent-encoding (%xy).
|
||||||
*/
|
*/
|
||||||
@ -4736,6 +4937,17 @@ conninfo_uri_parse_options(PQconninfoOption *options, const char *uri,
|
|||||||
char *user = NULL;
|
char *user = NULL;
|
||||||
char *host = NULL;
|
char *host = NULL;
|
||||||
bool retval = false;
|
bool retval = false;
|
||||||
|
PQExpBufferData hostbuf;
|
||||||
|
PQExpBufferData portbuf;
|
||||||
|
|
||||||
|
initPQExpBuffer(&hostbuf);
|
||||||
|
initPQExpBuffer(&portbuf);
|
||||||
|
if (PQExpBufferDataBroken(hostbuf) || PQExpBufferDataBroken(portbuf))
|
||||||
|
{
|
||||||
|
printfPQExpBuffer(errorMessage,
|
||||||
|
libpq_gettext("out of memory\n"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/* need a modifiable copy of the input URI */
|
/* need a modifiable copy of the input URI */
|
||||||
buf = strdup(uri);
|
buf = strdup(uri);
|
||||||
@ -4810,85 +5022,104 @@ conninfo_uri_parse_options(PQconninfoOption *options, const char *uri,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* "p" has been incremented past optional URI credential information at
|
* There may be multiple netloc[:port] pairs, each separated from the next
|
||||||
* this point and now points at the "netloc" part of the URI.
|
* by a comma. When we initially enter this loop, "p" has been
|
||||||
*
|
* incremented past optional URI credential information at this point and
|
||||||
* Look for IPv6 address.
|
* now points at the "netloc" part of the URI. On subsequent loop
|
||||||
|
* iterations, "p" has been incremented past the comma separator and now
|
||||||
|
* points at the start of the next "netloc".
|
||||||
*/
|
*/
|
||||||
if (*p == '[')
|
for (;;)
|
||||||
{
|
{
|
||||||
host = ++p;
|
|
||||||
while (*p && *p != ']')
|
|
||||||
++p;
|
|
||||||
if (!*p)
|
|
||||||
{
|
|
||||||
printfPQExpBuffer(errorMessage,
|
|
||||||
libpq_gettext("end of string reached when looking for matching \"]\" in IPv6 host address in URI: \"%s\"\n"),
|
|
||||||
uri);
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
if (p == host)
|
|
||||||
{
|
|
||||||
printfPQExpBuffer(errorMessage,
|
|
||||||
libpq_gettext("IPv6 host address may not be empty in URI: \"%s\"\n"),
|
|
||||||
uri);
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Cut off the bracket and advance */
|
|
||||||
*(p++) = '\0';
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The address may be followed by a port specifier or a slash or a
|
* Look for IPv6 address.
|
||||||
* query.
|
|
||||||
*/
|
*/
|
||||||
if (*p && *p != ':' && *p != '/' && *p != '?')
|
if (*p == '[')
|
||||||
{
|
{
|
||||||
printfPQExpBuffer(errorMessage,
|
host = ++p;
|
||||||
libpq_gettext("unexpected character \"%c\" at position %d in URI (expected \":\" or \"/\"): \"%s\"\n"),
|
while (*p && *p != ']')
|
||||||
*p, (int) (p - buf + 1), uri);
|
++p;
|
||||||
goto cleanup;
|
if (!*p)
|
||||||
|
{
|
||||||
|
printfPQExpBuffer(errorMessage,
|
||||||
|
libpq_gettext("end of string reached when looking for matching \"]\" in IPv6 host address in URI: \"%s\"\n"),
|
||||||
|
uri);
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
if (p == host)
|
||||||
|
{
|
||||||
|
printfPQExpBuffer(errorMessage,
|
||||||
|
libpq_gettext("IPv6 host address may not be empty in URI: \"%s\"\n"),
|
||||||
|
uri);
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Cut off the bracket and advance */
|
||||||
|
*(p++) = '\0';
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The address may be followed by a port specifier or a slash or a
|
||||||
|
* query or a separator comma.
|
||||||
|
*/
|
||||||
|
if (*p && *p != ':' && *p != '/' && *p != '?' && *p != ',')
|
||||||
|
{
|
||||||
|
printfPQExpBuffer(errorMessage,
|
||||||
|
libpq_gettext("unexpected character \"%c\" at position %d in URI (expected \":\" or \"/\"): \"%s\"\n"),
|
||||||
|
*p, (int) (p - buf + 1), uri);
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
else
|
||||||
else
|
{
|
||||||
{
|
/* not an IPv6 address: DNS-named or IPv4 netloc */
|
||||||
/* not an IPv6 address: DNS-named or IPv4 netloc */
|
host = p;
|
||||||
host = p;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Look for port specifier (colon) or end of host specifier (slash),
|
* Look for port specifier (colon) or end of host specifier (slash)
|
||||||
* or query (question mark).
|
* or query (question mark) or host separator (comma).
|
||||||
*/
|
*/
|
||||||
while (*p && *p != ':' && *p != '/' && *p != '?')
|
while (*p && *p != ':' && *p != '/' && *p != '?' && *p != ',')
|
||||||
++p;
|
++p;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Save the hostname terminator before we null it */
|
|
||||||
prevchar = *p;
|
|
||||||
*p = '\0';
|
|
||||||
|
|
||||||
if (*host &&
|
|
||||||
!conninfo_storeval(options, "host", host,
|
|
||||||
errorMessage, false, true))
|
|
||||||
goto cleanup;
|
|
||||||
|
|
||||||
|
|
||||||
if (prevchar == ':')
|
|
||||||
{
|
|
||||||
const char *port = ++p; /* advance past host terminator */
|
|
||||||
|
|
||||||
while (*p && *p != '/' && *p != '?')
|
|
||||||
++p;
|
|
||||||
|
|
||||||
|
/* Save the hostname terminator before we null it */
|
||||||
prevchar = *p;
|
prevchar = *p;
|
||||||
*p = '\0';
|
*p = '\0';
|
||||||
|
|
||||||
if (*port &&
|
appendPQExpBufferStr(&hostbuf, host);
|
||||||
!conninfo_storeval(options, "port", port,
|
|
||||||
errorMessage, false, true))
|
if (prevchar == ':')
|
||||||
goto cleanup;
|
{
|
||||||
|
const char *port = ++p; /* advance past host terminator */
|
||||||
|
|
||||||
|
while (*p && *p != '/' && *p != '?' && *p != ',')
|
||||||
|
++p;
|
||||||
|
|
||||||
|
prevchar = *p;
|
||||||
|
*p = '\0';
|
||||||
|
|
||||||
|
appendPQExpBufferStr(&portbuf, port);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prevchar != ',')
|
||||||
|
break;
|
||||||
|
++p; /* advance past comma separator */
|
||||||
|
appendPQExpBufferStr(&hostbuf, ",");
|
||||||
|
appendPQExpBufferStr(&portbuf, ",");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Save final values for host and port. */
|
||||||
|
if (PQExpBufferDataBroken(hostbuf) || PQExpBufferDataBroken(portbuf))
|
||||||
|
goto cleanup;
|
||||||
|
if (hostbuf.data[0] &&
|
||||||
|
!conninfo_storeval(options, "host", hostbuf.data,
|
||||||
|
errorMessage, false, true))
|
||||||
|
goto cleanup;
|
||||||
|
if (portbuf.data[0] &&
|
||||||
|
!conninfo_storeval(options, "port", portbuf.data,
|
||||||
|
errorMessage, false, true))
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
if (prevchar && prevchar != '?')
|
if (prevchar && prevchar != '?')
|
||||||
{
|
{
|
||||||
const char *dbname = ++p; /* advance past host terminator */
|
const char *dbname = ++p; /* advance past host terminator */
|
||||||
@ -4923,6 +5154,8 @@ conninfo_uri_parse_options(PQconninfoOption *options, const char *uri,
|
|||||||
retval = true;
|
retval = true;
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
|
termPQExpBuffer(&hostbuf);
|
||||||
|
termPQExpBuffer(&portbuf);
|
||||||
free(buf);
|
free(buf);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
@ -5342,9 +5575,15 @@ PQuser(const PGconn *conn)
|
|||||||
char *
|
char *
|
||||||
PQpass(const PGconn *conn)
|
PQpass(const PGconn *conn)
|
||||||
{
|
{
|
||||||
|
char *password = NULL;
|
||||||
|
|
||||||
if (!conn)
|
if (!conn)
|
||||||
return NULL;
|
return NULL;
|
||||||
return conn->pgpass;
|
if (conn->connhost != NULL)
|
||||||
|
password = conn->connhost[conn->whichhost].password;
|
||||||
|
if (password == NULL)
|
||||||
|
password = conn->pgpass;
|
||||||
|
return password;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *
|
char *
|
||||||
@ -5352,15 +5591,14 @@ PQhost(const PGconn *conn)
|
|||||||
{
|
{
|
||||||
if (!conn)
|
if (!conn)
|
||||||
return NULL;
|
return NULL;
|
||||||
if (conn->pghost != NULL && conn->pghost[0] != '\0')
|
if (conn->connhost != NULL)
|
||||||
|
return conn->connhost[conn->whichhost].host;
|
||||||
|
else if (conn->pghost != NULL && conn->pghost[0] != '\0')
|
||||||
return conn->pghost;
|
return conn->pghost;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
#ifdef HAVE_UNIX_SOCKETS
|
#ifdef HAVE_UNIX_SOCKETS
|
||||||
if (conn->pgunixsocket != NULL && conn->pgunixsocket[0] != '\0')
|
return DEFAULT_PGSOCKET_DIR;
|
||||||
return conn->pgunixsocket;
|
|
||||||
else
|
|
||||||
return DEFAULT_PGSOCKET_DIR;
|
|
||||||
#else
|
#else
|
||||||
return DefaultHost;
|
return DefaultHost;
|
||||||
#endif
|
#endif
|
||||||
@ -5372,6 +5610,8 @@ PQport(const PGconn *conn)
|
|||||||
{
|
{
|
||||||
if (!conn)
|
if (!conn)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
if (conn->connhost != NULL)
|
||||||
|
return conn->connhost[conn->whichhost].port;
|
||||||
return conn->pgport;
|
return conn->pgport;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5481,10 +5721,13 @@ PQbackendPID(const PGconn *conn)
|
|||||||
int
|
int
|
||||||
PQconnectionNeedsPassword(const PGconn *conn)
|
PQconnectionNeedsPassword(const PGconn *conn)
|
||||||
{
|
{
|
||||||
|
char *password;
|
||||||
|
|
||||||
if (!conn)
|
if (!conn)
|
||||||
return false;
|
return false;
|
||||||
|
password = PQpass(conn);
|
||||||
if (conn->password_needed &&
|
if (conn->password_needed &&
|
||||||
(conn->pgpass == NULL || conn->pgpass[0] == '\0'))
|
(password == NULL || password[0] == '\0'))
|
||||||
return true;
|
return true;
|
||||||
else
|
else
|
||||||
return false;
|
return false;
|
||||||
|
@ -292,6 +292,30 @@ typedef struct pgDataValue
|
|||||||
const char *value; /* data value, without zero-termination */
|
const char *value; /* data value, without zero-termination */
|
||||||
} PGdataValue;
|
} 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
|
* PGconn stores all the state data associated with a single connection
|
||||||
* to a backend.
|
* to a backend.
|
||||||
@ -299,13 +323,15 @@ typedef struct pgDataValue
|
|||||||
struct pg_conn
|
struct pg_conn
|
||||||
{
|
{
|
||||||
/* Saved values of connection options */
|
/* 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
|
char *pghostaddr; /* the numeric IP address of the machine on
|
||||||
* which the server is running. Takes
|
* which the server is running. Takes
|
||||||
* precedence over above. */
|
* precedence over above. */
|
||||||
char *pgport; /* the server's communication port number */
|
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
|
char *pgtty; /* tty on which the backend messages is
|
||||||
* displayed (OBSOLETE, NOT USED) */
|
* displayed (OBSOLETE, NOT USED) */
|
||||||
char *connect_timeout; /* connection timeout (numeric string) */
|
char *connect_timeout; /* connection timeout (numeric string) */
|
||||||
@ -363,6 +389,11 @@ struct pg_conn
|
|||||||
PGnotify *notifyHead; /* oldest unreported Notify msg */
|
PGnotify *notifyHead; /* oldest unreported Notify msg */
|
||||||
PGnotify *notifyTail; /* newest 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 */
|
/* Connection data */
|
||||||
pgsocket sock; /* FD for socket, PGINVALID_SOCKET if
|
pgsocket sock; /* FD for socket, PGINVALID_SOCKET if
|
||||||
* unconnected */
|
* unconnected */
|
||||||
@ -378,9 +409,7 @@ struct pg_conn
|
|||||||
bool sigpipe_flag; /* can we mask SIGPIPE via MSG_NOSIGNAL? */
|
bool sigpipe_flag; /* can we mask SIGPIPE via MSG_NOSIGNAL? */
|
||||||
|
|
||||||
/* Transient state needed while establishing connection */
|
/* Transient state needed while establishing connection */
|
||||||
struct addrinfo *addrlist; /* list of possible backend addresses */
|
struct addrinfo *addr_cur; /* backend address currently being tried */
|
||||||
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? */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user