git.postgresql.org Git - postgresql.git/commitdiff (original) (raw)

Several libpq</> functions parse a user-specified string to obtain

connection parameters. There are two accepted formats for these strings:

plain keyword = value strings

- and RFC

- 3986 URIs.

+ and URIs. URIs generally follow

+ RFC

+ 3986, except that multi-host connection strings are allowed

+ as further described below.

The general form for a connection URI is:

-postgresql://[user[:password]@][netloc][:port][/dbname][?param1=value1&...]

+postgresql://[user[:password]@][netloc][:port][,...][/dbname][?param1=value1&...]

postgresql://user@localhost

postgresql://user:secret@localhost

postgresql://other@localhost/otherdb?connect_timeout=10&application_name=myapp

+postgresql://host1:123,host2:456/somedb

Components of the hierarchical part of the URI can also

be given as parameters. For example:

postgresql://%2Fvar%2Flib%2Fpostgresql/dbname

+

+ It is possible to specify multiple host components, each with an optional

+ port component, in a single URI. A URI of the form

+ postgresql://host1:port1,host2:port2,host3:port3/

+ is equivalent to a connection string of the form

+ host=host1,host2,host3 port=port1,port2,port3. Each

+ host will be tried in turn until a connection is successfully established.

+

host

- Name of host to connect to.host name</></>

- If this begins with a slash, it specifies Unix-domain

+ Comma-separated list of host names.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 host 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 host is

+ not specified is to connect to a Unix-domain

socketUnix domain socket</></> in

/tmp (or whatever socket directory was specified

when PostgreSQL</> was built). On machines without

Port number to connect to at the server host, or socket file

name extension for Unix-domain

connections.port</></>

+ If the 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.

The following functions return parameter values established at connection.

- These values are fixed for the life of the PGconn</> object.

+ These values are fixed for the life of the connection. If a multi-host

+ connection string is used, the values of PQhost</>,

+ PQport</>, and PQpass</> can change if a new connection

+ is established using the same PGconn</> object. Other values

+ are fixed for the lifetime of the PGconn</> object.

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)

static bool

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

* fail, since the only way we get here is if pg_fe_getauthname() failed

}

/*

- * 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')

{

+ int i;

if (conn->pgpass)

free(conn->pgpass);

- conn->pgpass = PasswordFromFile(conn->pghost, conn->pgport,

- conn->dbName, conn->pguser);

- if (conn->pgpass == NULL)

+ conn->pgpass = strdup(DefaultPassword);

+ if (!conn->pgpass)

+ goto oom_error;

+ for (i = 0; i < conn->nconnhost; ++i)

{

- conn->pgpass = strdup(DefaultPassword);

- if (!conn->pgpass)

- goto oom_error;

+ conn->connhost[i].password =

+ PasswordFromFile(conn->connhost[i].host,

+ 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;

}

/*

{

char host_addr[NI_MAXHOST];

const char *displayed_host;

+ const char *displayed_port;

struct sockaddr_storage *addr = &conn->raddr.addr;

/*

else

strcpy(host_addr, "???");

- if (conn->pghostaddr && conn->pghostaddr[0] != '\0')

- displayed_host = conn->pghostaddr;

- else if (conn->pghost && conn->pghost[0] != '\0')

- displayed_host = conn->pghost;

- else

- displayed_host = DefaultHost;

+ /* To which host and port were we actually connecting? */

+ displayed_host = conn->connhost[conn->whichhost].host;

+ displayed_port = conn->connhost[conn->whichhost].port;

+ if (displayed_port == NULL || displayed_port[0] == '\0')

+ displayed_port = DEF_PGPORT_STR;

/*

* If the user did not supply an IP address using 'hostaddr', and

SOCK_STRERROR(errorno, sebuf, sizeof(sebuf)),

displayed_host,

host_addr,

- conn->pgport);

+ displayed_port);

else

appendPQExpBuffer(&conn->errorMessage,

libpq_gettext("could not connect to server: %s\n"

"\tTCP/IP connections on port %s?\n"),

SOCK_STRERROR(errorno, sebuf, sizeof(sebuf)),

displayed_host,

- conn->pgport);

+ displayed_port);

}

}

static int

connectDBStart(PGconn *conn)

{

- int portnum;

char portstr[MAXPGPATH];

- struct addrinfo *addrs = NULL;

- struct addrinfo hint;

- const char *node;

int ret;

+ int i;

if (!conn)

return 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)

+ {

+ pg_conn_host *ch = &conn->connhost[i];

+ char *node = ch->host;

+ struct addrinfo hint;

+ int thisport;

- /* Initialize hint structure */

- MemSet(&hint, 0, sizeof(hint));

- hint.ai_socktype = SOCK_STREAM;

- hint.ai_family = AF_UNSPEC;

+ /* 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);

- if (portnum < 1 || portnum > 65535)

+ /* Figure out the port number we're going to use. */

+ if (ch->port == NULL)

+ thisport = DEF_PGPORT;

+ else

{

- appendPQExpBuffer(&conn->errorMessage,

+ thisport = atoi(ch->port);

+ if (thisport < 1 || thisport > 65535)

+ {

+ appendPQExpBuffer(&conn->errorMessage,

libpq_gettext("invalid port number: \"%s\"\n"),

- conn->pgport);

- conn->options_valid = false;

- goto connect_errReturn;

+ ch->port);

+ conn->options_valid = false;

+ goto connect_errReturn;

+ }

}

- }

- else

- portnum = DEF_PGPORT;

- snprintf(portstr, sizeof(portstr), "%d", portnum);

+ snprintf(portstr, sizeof(portstr), "%d", thisport);

- if (conn->pghostaddr != NULL && conn->pghostaddr[0] != '\0')

- {

- /* Using pghostaddr avoids a hostname lookup */

- node = conn->pghostaddr;

- 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

- {

+ /* 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

- /* 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)

+ 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)

{

- appendPQExpBuffer(&conn->errorMessage,

- libpq_gettext("Unix-domain socket path \"%s\" is too long (maximum %d bytes)\n"),

- portstr,

- (int) (UNIXSOCK_PATH_BUFLEN - 1));

+ 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;

}

-#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 */

- ret = pg_getaddrinfo_all(node, portstr, &hint, &addrs);

- if (ret || !addrs)

- {

- 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 (addrs)

- pg_freeaddrinfo_all(hint.ai_family, addrs);

- conn->options_valid = false;

- goto connect_errReturn;

}

#ifdef USE_SSL

/*

* Set up to try to connect, with protocol 3.0 as the first attempt.

*/

- conn->addrlist = addrs;

- conn->addr_cur = addrs;

- conn->addrlist_family = hint.ai_family;

+ conn->whichhost = 0;

+ conn->addr_cur = conn->connhost[0].addrlist;

conn->pversion = PG_PROTOCOL(3, 0);

conn->send_appname = true;

conn->status = CONNECTION_NEEDED;

* returned by pg_getaddrinfo_all(). conn->addr_cur is the

* 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 */

+ addr_cur = conn->addr_cur;

memcpy(&conn->raddr.addr, addr_cur->ai_addr,

addr_cur->ai_addrlen);

conn->raddr.salen = addr_cur->ai_addrlen;

* ignore socket() failure if we have more addresses

* 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;

continue;

* If more addresses remain, keep trying, just as in the

* 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->status = CONNECTION_NEEDED;

goto error_return;

}

- /* We can release the address list now. */

- pg_freeaddrinfo_all(conn->addrlist_family, conn->addrlist);

- conn->addrlist = NULL;

+ /* We can release the address lists now. */

+ if (conn->connhost != 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;

/* Fire up post-connection housekeeping if needed */

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)

free(conn->client_encoding_initial);

if (conn->events)

free(conn->pghostaddr);

if (conn->pgport)

free(conn->pgport);

- if (conn->pgunixsocket)

- free(conn->pgunixsocket);

if (conn->pgtty)

free(conn->pgtty);

if (conn->connect_timeout)

conn->asyncStatus = PGASYNC_IDLE;

pqClearAsyncResult(conn); /* deallocate result */

resetPQExpBuffer(&conn->errorMessage);

- pg_freeaddrinfo_all(conn->addrlist_family, conn->addrlist);

- conn->addrlist = NULL;

+ if (conn->connhost != 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;

notify = conn->notifyHead;

while (notify != NULL)

* postgresql://[user[:password]@][netloc][:port][/dbname][?param1=value1&...]

*

* 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).

*/

char *user = NULL;

char *host = NULL;

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 */

buf = strdup(uri);

}

/*

- * "p" has been incremented past optional URI credential information at

- * this point and now points at the "netloc" part of the URI.

- *

- * Look for IPv6 address.

+ * There may be multiple netloc[:port] pairs, each separated from the next

+ * by a comma. When we initially enter this loop, "p" has been

+ * incremented past optional URI credential information at this point and

+ * 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

- * query.

+ * Look for IPv6 address.

*/

- if (*p && *p != ':' && *p != '/' && *p != '?')

+ if (*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;

+ 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

+ * 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

- {

- /* not an IPv6 address: DNS-named or IPv4 netloc */

- host = p;

+ else

+ {

+ /* not an IPv6 address: DNS-named or IPv4 netloc */

+ host = p;

- /*

- * Look for port specifier (colon) or end of host specifier (slash),

- * or query (question mark).

- */

- while (*p && *p != ':' && *p != '/' && *p != '?')

- ++p;

- }

+ /*

+ * Look for port specifier (colon) or end of host specifier (slash)

+ * or query (question mark) or host separator (comma).

+ */

+ while (*p && *p != ':' && *p != '/' && *p != '?' && *p != ',')

+ ++p;

+ }

- /* Save the hostname terminator before we null it */

- prevchar = *p;

- *p = '\0';

+ /* Save the hostname terminator before we null it */

+ prevchar = *p;

+ *p = '\0';

- if (*host &&

- !conninfo_storeval(options, "host", host,

- errorMessage, false, true))

- goto cleanup;

+ appendPQExpBufferStr(&hostbuf, host);

+ if (prevchar == ':')

+ {

+ const char *port = ++p; /* advance past host terminator */

- if (prevchar == ':')

- {

- const char *port = ++p; /* advance past host terminator */

+ while (*p && *p != '/' && *p != '?' && *p != ',')

+ ++p;

- while (*p && *p != '/' && *p != '?')

- ++p;

+ prevchar = *p;

+ *p = '\0';

- prevchar = *p;

- *p = '\0';

+ appendPQExpBufferStr(&portbuf, port);

+ }

- if (*port &&

- !conninfo_storeval(options, "port", port,

- errorMessage, false, true))

- goto cleanup;

+ 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 != '?')

{

const char *dbname = ++p; /* advance past host terminator */

retval = true;

cleanup:

+ termPQExpBuffer(&hostbuf);

+ termPQExpBuffer(&portbuf);

free(buf);

return retval;

}

char *

PQpass(const PGconn *conn)

{

+ char *password = NULL;

if (!conn)

return NULL;

- return conn->pgpass;

+ if (conn->connhost != NULL)

+ password = conn->connhost[conn->whichhost].password;

+ if (password == NULL)

+ password = conn->pgpass;

+ return password;

}

char *

{

if (!conn)

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;

else

{

#ifdef HAVE_UNIX_SOCKETS

- if (conn->pgunixsocket != NULL && conn->pgunixsocket[0] != '\0')

- return conn->pgunixsocket;

- else

- return DEFAULT_PGSOCKET_DIR;

+ return DEFAULT_PGSOCKET_DIR;

#else

return DefaultHost;

#endif

{

if (!conn)

return NULL;

+ if (conn->connhost != NULL)

+ return conn->connhost[conn->whichhost].port;

return conn->pgport;

}

int

PQconnectionNeedsPassword(const PGconn *conn)

{

+ char *password;

if (!conn)

return false;

+ password = PQpass(conn);

if (conn->password_needed &&

- (conn->pgpass == NULL || conn->pgpass[0] == '\0'))

+ (password == NULL || password[0] == '\0'))

return true;

else

return false;

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.

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) */

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 */

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? */