diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index 0ec501e5bd..8a820ac007 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -116,10 +116,16 @@ PGconn *PQconnectdbParams(const char * const *keywords,
programming.
+
+ The currently recognized parameter key words are listed in
+ .
+
+
When expand_dbname is non-zero, the
dbname key word value is allowed to be recognized
- as a conninfo string. See below for details.
+ as a connection string. More details on the possible formats appear in
+ .
@@ -130,507 +136,6 @@ PGconn *PQconnectdbParams(const char * const *keywords,
- The currently recognized parameter key words are:
-
-
-
- host
-
-
- Name of host to connect to.host name>>
- If this 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
- socketUnix domain socket>> in
- /tmp (or whatever socket directory was specified
- when PostgreSQL> was built). On machines without
- Unix-domain sockets, the default is to connect to localhost>.
-
-
-
-
-
- hostaddr
-
-
- Numeric IP address of host to connect to. This should be in the
- standard IPv4 address format, e.g., 172.28.40.9>. If
- your machine supports IPv6, you can also use those addresses.
- TCP/IP communication is
- always used when a nonempty string is specified for this parameter.
-
-
-
- Using hostaddr> instead of host> allows the
- application to avoid a host name look-up, which might be important
- in applications with time constraints. However, a host name is
- required for Kerberos, GSSAPI, or SSPI authentication
- methods, as well as for verify-full> SSL
- certificate verification. The following rules are used:
-
-
-
- If host> is specified without hostaddr>,
- a host name lookup occurs.
-
-
-
-
- If hostaddr> is specified without host>,
- the value for hostaddr> gives the server network address.
- The connection attempt will fail if the authentication
- method requires a host name.
-
-
-
-
- If both host> and hostaddr> are specified,
- the value for hostaddr> gives the server network address.
- The value for host> is ignored unless the
- authentication method requires it, in which case it will be
- used as the host name.
-
-
-
- Note that authentication is likely to fail if host>
- is not the name of the server at network address hostaddr>.
- Also, note that host> rather than hostaddr>
- is used to identify the connection in ~/.pgpass> (see
- ).
-
-
-
- Without either a host name or host address,
- libpq will connect using a
- local Unix-domain socket; or on machines without Unix-domain
- sockets, it will attempt to connect to localhost>.
-
-
-
-
-
- port
-
-
- Port number to connect to at the server host, or socket file
- name extension for Unix-domain
- connections.port>>
-
-
-
-
-
- dbname
-
-
- The database name. Defaults to be the same as the user name.
-
-
-
-
-
- user
-
-
- PostgreSQL user name to connect as.
- Defaults to be the same as the operating system name of the user
- running the application.
-
-
-
-
-
- password
-
-
- Password to be used if the server demands password authentication.
-
-
-
-
-
- connect_timeout
-
-
- Maximum wait for connection, in seconds (write as a decimal integer
- string). Zero or not specified means wait indefinitely. It is not
- recommended to use a timeout of less than 2 seconds.
-
-
-
-
-
- client_encoding
-
-
- This sets the client_encoding
- configuration parameter for this connection. In addition to
- the values accepted by the corresponding server option, you
- can use auto to determine the right
- encoding from the current locale in the client
- (LC_CTYPE environment variable on Unix
- systems).
-
-
-
-
-
- options
-
-
- Adds command-line options to send to the server at run-time.
- For example, setting this to -c geqo=off> sets the
- session's value of the geqo> parameter to
- off>. For a detailed discussion of the available
- options, consult .
-
-
-
-
-
- application_name
-
-
- Specifies a value for the
- configuration parameter.
-
-
-
-
-
- fallback_application_name
-
-
- Specifies a fallback value for the configuration parameter.
- This value will be used if no value has been given for
- application_name> via a connection parameter or the
- PGAPPNAME environment variable. Specifying
- a fallback name is useful in generic utility programs that
- wish to set a default application name but allow it to be
- overridden by the user.
-
-
-
-
-
- keepalives
-
-
- Controls whether client-side TCP keepalives are used. The default
- value is 1, meaning on, but you can change this to 0, meaning off,
- if keepalives are not wanted. This parameter is ignored for
- connections made via a Unix-domain socket.
-
-
-
-
-
- keepalives_idle
-
-
- Controls the number of seconds of inactivity after which TCP should
- send a keepalive message to the server. A value of zero uses the
- system default. This parameter is ignored for connections made via a
- Unix-domain socket, or if keepalives are disabled. It is only supported
- on systems where the TCP_KEEPIDLE> or TCP_KEEPALIVE>
- socket option is available, and on Windows; on other systems, it has no
- effect.
-
-
-
-
-
- keepalives_interval
-
-
- Controls the number of seconds after which a TCP keepalive message
- that is not acknowledged by the server should be retransmitted. A
- value of zero uses the system default. This parameter is ignored for
- connections made via a Unix-domain socket, or if keepalives are disabled.
- It is only supported on systems where the TCP_KEEPINTVL>
- socket option is available, and on Windows; on other systems, it has no
- effect.
-
-
-
-
-
- keepalives_count
-
-
- Controls the number of TCP keepalives that can be lost before the
- client's connection to the server is considered dead. A value of
- zero uses the system default. This parameter is ignored for
- connections made via a Unix-domain socket, or if keepalives are disabled.
- It is only supported on systems where the TCP_KEEPCNT>
- socket option is available; on other systems, it has no effect.
-
-
-
-
-
- tty
-
-
- Ignored (formerly, this specified where to send server debug output).
-
-
-
-
-
- sslmode
-
-
- This option determines whether or with what priority a secure
- SSL> TCP/IP connection will be negotiated with the
- server. There are six modes:
-
-
-
- disable
-
-
- only try a non-SSL> connection
-
-
-
-
-
- allow
-
-
- first try a non-SSL> connection; if that
- fails, try an SSL> connection
-
-
-
-
-
- prefer (default)
-
-
- first try an SSL> connection; if that fails,
- try a non-SSL> connection
-
-
-
-
-
- require
-
-
- only try an SSL> connection. If a root CA
- file is present, verify the certificate in the same way as
- if verify-ca was specified
-
-
-
-
-
- verify-ca
-
-
- only try an SSL> connection, and verify that
- the server certificate is issued by a trusted
- certificate authority (CA>)
-
-
-
-
-
- verify-full
-
-
- only try an SSL> connection, verify that the
- server certificate is issued by a
- trusted CA> and that the server host name
- matches that in the certificate
-
-
-
-
-
- See for a detailed description of how
- these options work.
-
-
-
- sslmode> is ignored for Unix domain socket
- communication.
- If PostgreSQL> is compiled without SSL support,
- using options require>, verify-ca>, or
- verify-full> will cause an error, while
- options allow> and prefer> will be
- accepted but libpq> will not actually attempt
- an SSL>
- connection.SSL>with libpq>
-
-
-
-
-
- requiressl
-
-
- This option is deprecated in favor of the sslmode>
- setting.
-
-
-
- If set to 1, an SSL connection to the server
- is required (this is equivalent to sslmode>
- require>). libpq> will then refuse
- to connect if the server does not accept an
- SSL connection. If set to 0 (default),
- libpq> will negotiate the connection type with
- the server (equivalent to sslmode>
- prefer>). This option is only available if
- PostgreSQL> is compiled with SSL support.
-
-
-
-
-
- sslcompression
-
-
- If set to 1 (default), data sent over SSL connections will be
- compressed (this requires OpenSSL> version
- 0.9.8 or later).
- If set to 0, compression will be disabled (this requires
- OpenSSL> 1.0.0 or later).
- This parameter is ignored if a connection without SSL is made,
- or if the version of OpenSSL> used does not support
- it.
-
-
- Compression uses CPU time, but can improve throughput if
- the network is the bottleneck.
- Disabling compression can improve response time and throughput
- if CPU performance is the limiting factor.
-
-
-
-
-
- sslcert
-
-
- This parameter specifies the file name of the client SSL
- certificate, replacing the default
- ~/.postgresql/postgresql.crt>.
- This parameter is ignored if an SSL connection is not made.
-
-
-
-
-
- sslkey
-
-
- This parameter specifies the location for the secret key used for
- the client certificate. It can either specify a file name that will
- be used instead of the default
- ~/.postgresql/postgresql.key>, or it can specify a key
- obtained from an external engine> (engines are
- OpenSSL> loadable modules). An external engine
- specification should consist of a colon-separated engine name and
- an engine-specific key identifier. This parameter is ignored if an
- SSL connection is not made.
-
-
-
-
-
- sslrootcert
-
-
- This parameter specifies the name of a file containing SSL
- certificate authority (CA>) certificate(s).
- If the file exists, the server's certificate will be verified
- to be signed by one of these authorities. The default is
- ~/.postgresql/root.crt>.
-
-
-
-
-
- sslcrl
-
-
- This parameter specifies the file name of the SSL certificate
- revocation list (CRL). Certificates listed in this file, if it
- exists, will be rejected while attempting to authenticate the
- server's certificate. The default is
- ~/.postgresql/root.crl>.
-
-
-
-
-
- requirepeer
-
-
- This parameter specifies the operating-system user name of the
- server, for example requirepeer=postgres.
- When making a Unix-domain socket connection, if this
- parameter is set, the client checks at the beginning of the
- connection that the server process is running under the specified
- user name; if it is not, the connection is aborted with an error.
- This parameter can be used to provide server authentication similar
- to that available with SSL certificates on TCP/IP connections.
- (Note that if the Unix-domain socket is in
- /tmp or another publicly writable location,
- any user could start a server listening there. Use this parameter
- to ensure that you are connected to a server run by a trusted user.)
- This option is only supported on platforms for which the
- peer> authentication method is implemented; see
- .
-
-
-
-
-
- krbsrvname
-
-
- Kerberos service name to use when authenticating with Kerberos 5
- or GSSAPI.
- This must match the service name specified in the server
- configuration for Kerberos authentication to succeed. (See also
- and .)
-
-
-
-
-
- gsslib
-
-
- GSS library to use for GSSAPI authentication. Only used on Windows.
- Set to gssapi to force libpq to use the GSSAPI
- library for authentication instead of the default SSPI.
-
-
-
-
-
- service
-
-
- Service name to use for additional parameters. It specifies a service
- name in pg_service.conf that holds additional connection parameters.
- This allows applications to specify only a service name so connection parameters
- can be centrally maintained. See .
-
-
-
-
-
If any parameter is unspecified, then the corresponding
environment variable (see )
is checked. If the environment variable is not set either,
@@ -638,20 +143,11 @@ PGconn *PQconnectdbParams(const char * const *keywords,
- If expand_dbname is non-zero and
- dbname contains an = sign, it
- is taken as a conninfo string in exactly the same way as
- if it had been passed to PQconnectdb(see below). Previously
- processed key words will be overridden by key words in the
- conninfo string.
-
-
-
- In general key words are processed from the beginning of these arrays in index
- order. The effect of this is that when key words are repeated, the last processed
- value is retained. Therefore, through careful placement of the
- dbname key word, it is possible to determine what may
- be overridden by a conninfo string, and what may not.
+ In general key words are processed from the beginning of these arrays in index
+ order. The effect of this is that when key words are repeated, the last processed
+ value is retained. Therefore, through careful placement of the
+ dbname key word, it is possible to determine what may
+ be overridden by a conninfo string, and what may not.
@@ -675,19 +171,13 @@ PGconn *PQconnectdb(const char *conninfo);
The passed string can be empty to use all default parameters, or it can
- contain one or more parameter settings separated by whitespace.
- Each parameter setting is in the form keyword = value.
- Spaces around the equal sign are optional. To write an empty value,
- or a value containing spaces, surround it with single quotes, e.g.,
- keyword = 'a value'. Single quotes and backslashes
- within the value must be escaped with a backslash, i.e.,
- \' and \\.
-
+ contain one or more parameter settings separated by whitespace,
+ or it can contain a URI.
+ See for details.
+
-
- The currently recognized parameter key words are the same as above.
-
-
+
+
@@ -714,10 +204,11 @@ PGconn *PQsetdbLogin(const char *pghost,
- If the dbName contains an = sign, it
+ If the dbName contains
+ an = sign or has a valid connection URI prefix, it
is taken as a conninfo string in exactly the same way as
if it had been passed to PQconnectdb, and the remaining
- parameters are then applied as above.
+ parameters are then applied as specified for PQconnectdbParams>.
@@ -795,7 +286,7 @@ PostgresPollingStatusType PQconnectPoll(PGconn *conn);
The hostaddr> and host> parameters are used appropriately to ensure that
name and reverse name queries are not made. See the documentation of
- these parameters under PQconnectdbParams above for details.
+ these parameters in for details.
@@ -1219,6 +710,617 @@ PGPing PQping(const char *conninfo);
+
+
+ Parameter Key Words
+
+
+ The currently recognized parameter key words are:
+
+
+
+ host
+
+
+ Name of host to connect to.host name>>
+ If this 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
+ socketUnix domain socket>> in
+ /tmp (or whatever socket directory was specified
+ when PostgreSQL> was built). On machines without
+ Unix-domain sockets, the default is to connect to localhost>.
+
+
+
+
+
+ hostaddr
+
+
+ Numeric IP address of host to connect to. This should be in the
+ standard IPv4 address format, e.g., 172.28.40.9>. If
+ your machine supports IPv6, you can also use those addresses.
+ TCP/IP communication is
+ always used when a nonempty string is specified for this parameter.
+
+
+
+ Using hostaddr> instead of host> allows the
+ application to avoid a host name look-up, which might be important
+ in applications with time constraints. However, a host name is
+ required for Kerberos, GSSAPI, or SSPI authentication
+ methods, as well as for verify-full> SSL
+ certificate verification. The following rules are used:
+
+
+
+ If host> is specified without hostaddr>,
+ a host name lookup occurs.
+
+
+
+
+ If hostaddr> is specified without host>,
+ the value for hostaddr> gives the server network address.
+ The connection attempt will fail if the authentication
+ method requires a host name.
+
+
+
+
+ If both host> and hostaddr> are specified,
+ the value for hostaddr> gives the server network address.
+ The value for host> is ignored unless the
+ authentication method requires it, in which case it will be
+ used as the host name.
+
+
+
+ Note that authentication is likely to fail if host>
+ is not the name of the server at network address hostaddr>.
+ Also, note that host> rather than hostaddr>
+ is used to identify the connection in ~/.pgpass> (see
+ ).
+
+
+
+ Without either a host name or host address,
+ libpq will connect using a
+ local Unix-domain socket; or on machines without Unix-domain
+ sockets, it will attempt to connect to localhost>.
+
+
+
+
+
+ port
+
+
+ Port number to connect to at the server host, or socket file
+ name extension for Unix-domain
+ connections.port>>
+
+
+
+
+
+ dbname
+
+
+ The database name. Defaults to be the same as the user name.
+ In certain contexts, the value is checked for extended
+ formats; see for more details on
+ those.
+
+
+
+
+
+ user
+
+
+ PostgreSQL user name to connect as.
+ Defaults to be the same as the operating system name of the user
+ running the application.
+
+
+
+
+
+ password
+
+
+ Password to be used if the server demands password authentication.
+
+
+
+
+
+ connect_timeout
+
+
+ Maximum wait for connection, in seconds (write as a decimal integer
+ string). Zero or not specified means wait indefinitely. It is not
+ recommended to use a timeout of less than 2 seconds.
+
+
+
+
+
+ client_encoding
+
+
+ This sets the client_encoding
+ configuration parameter for this connection. In addition to
+ the values accepted by the corresponding server option, you
+ can use auto to determine the right
+ encoding from the current locale in the client
+ (LC_CTYPE environment variable on Unix
+ systems).
+
+
+
+
+
+ options
+
+
+ Adds command-line options to send to the server at run-time.
+ For example, setting this to -c geqo=off> sets the
+ session's value of the geqo> parameter to
+ off>. For a detailed discussion of the available
+ options, consult .
+
+
+
+
+
+ application_name
+
+
+ Specifies a value for the
+ configuration parameter.
+
+
+
+
+
+ fallback_application_name
+
+
+ Specifies a fallback value for the configuration parameter.
+ This value will be used if no value has been given for
+ application_name> via a connection parameter or the
+ PGAPPNAME environment variable. Specifying
+ a fallback name is useful in generic utility programs that
+ wish to set a default application name but allow it to be
+ overridden by the user.
+
+
+
+
+
+ keepalives
+
+
+ Controls whether client-side TCP keepalives are used. The default
+ value is 1, meaning on, but you can change this to 0, meaning off,
+ if keepalives are not wanted. This parameter is ignored for
+ connections made via a Unix-domain socket.
+
+
+
+
+
+ keepalives_idle
+
+
+ Controls the number of seconds of inactivity after which TCP should
+ send a keepalive message to the server. A value of zero uses the
+ system default. This parameter is ignored for connections made via a
+ Unix-domain socket, or if keepalives are disabled. It is only supported
+ on systems where the TCP_KEEPIDLE> or TCP_KEEPALIVE>
+ socket option is available, and on Windows; on other systems, it has no
+ effect.
+
+
+
+
+
+ keepalives_interval
+
+
+ Controls the number of seconds after which a TCP keepalive message
+ that is not acknowledged by the server should be retransmitted. A
+ value of zero uses the system default. This parameter is ignored for
+ connections made via a Unix-domain socket, or if keepalives are disabled.
+ It is only supported on systems where the TCP_KEEPINTVL>
+ socket option is available, and on Windows; on other systems, it has no
+ effect.
+
+
+
+
+
+ keepalives_count
+
+
+ Controls the number of TCP keepalives that can be lost before the
+ client's connection to the server is considered dead. A value of
+ zero uses the system default. This parameter is ignored for
+ connections made via a Unix-domain socket, or if keepalives are disabled.
+ It is only supported on systems where the TCP_KEEPCNT>
+ socket option is available; on other systems, it has no effect.
+
+
+
+
+
+ tty
+
+
+ Ignored (formerly, this specified where to send server debug output).
+
+
+
+
+
+ sslmode
+
+
+ This option determines whether or with what priority a secure
+ SSL> TCP/IP connection will be negotiated with the
+ server. There are six modes:
+
+
+
+ disable
+
+
+ only try a non-SSL> connection
+
+
+
+
+
+ allow
+
+
+ first try a non-SSL> connection; if that
+ fails, try an SSL> connection
+
+
+
+
+
+ prefer (default)
+
+
+ first try an SSL> connection; if that fails,
+ try a non-SSL> connection
+
+
+
+
+
+ require
+
+
+ only try an SSL> connection. If a root CA
+ file is present, verify the certificate in the same way as
+ if verify-ca was specified
+
+
+
+
+
+ verify-ca
+
+
+ only try an SSL> connection, and verify that
+ the server certificate is issued by a trusted
+ certificate authority (CA>)
+
+
+
+
+
+ verify-full
+
+
+ only try an SSL> connection, verify that the
+ server certificate is issued by a
+ trusted CA> and that the server host name
+ matches that in the certificate
+
+
+
+
+
+ See for a detailed description of how
+ these options work.
+
+
+
+ sslmode> is ignored for Unix domain socket
+ communication.
+ If PostgreSQL> is compiled without SSL support,
+ using options require>, verify-ca>, or
+ verify-full> will cause an error, while
+ options allow> and prefer> will be
+ accepted but libpq> will not actually attempt
+ an SSL>
+ connection.SSL>with libpq>
+
+
+
+
+
+ requiressl
+
+
+ This option is deprecated in favor of the sslmode>
+ setting.
+
+
+
+ If set to 1, an SSL connection to the server
+ is required (this is equivalent to sslmode>
+ require>). libpq> will then refuse
+ to connect if the server does not accept an
+ SSL connection. If set to 0 (default),
+ libpq> will negotiate the connection type with
+ the server (equivalent to sslmode>
+ prefer>). This option is only available if
+ PostgreSQL> is compiled with SSL support.
+
+
+
+
+
+ sslcompression
+
+
+ If set to 1 (default), data sent over SSL connections will be
+ compressed (this requires OpenSSL> version
+ 0.9.8 or later).
+ If set to 0, compression will be disabled (this requires
+ OpenSSL> 1.0.0 or later).
+ This parameter is ignored if a connection without SSL is made,
+ or if the version of OpenSSL> used does not support
+ it.
+
+
+ Compression uses CPU time, but can improve throughput if
+ the network is the bottleneck.
+ Disabling compression can improve response time and throughput
+ if CPU performance is the limiting factor.
+
+
+
+
+
+ sslcert
+
+
+ This parameter specifies the file name of the client SSL
+ certificate, replacing the default
+ ~/.postgresql/postgresql.crt>.
+ This parameter is ignored if an SSL connection is not made.
+
+
+
+
+
+ sslkey
+
+
+ This parameter specifies the location for the secret key used for
+ the client certificate. It can either specify a file name that will
+ be used instead of the default
+ ~/.postgresql/postgresql.key>, or it can specify a key
+ obtained from an external engine> (engines are
+ OpenSSL> loadable modules). An external engine
+ specification should consist of a colon-separated engine name and
+ an engine-specific key identifier. This parameter is ignored if an
+ SSL connection is not made.
+
+
+
+
+
+ sslrootcert
+
+
+ This parameter specifies the name of a file containing SSL
+ certificate authority (CA>) certificate(s).
+ If the file exists, the server's certificate will be verified
+ to be signed by one of these authorities. The default is
+ ~/.postgresql/root.crt>.
+
+
+
+
+
+ sslcrl
+
+
+ This parameter specifies the file name of the SSL certificate
+ revocation list (CRL). Certificates listed in this file, if it
+ exists, will be rejected while attempting to authenticate the
+ server's certificate. The default is
+ ~/.postgresql/root.crl>.
+
+
+
+
+
+ requirepeer
+
+
+ This parameter specifies the operating-system user name of the
+ server, for example requirepeer=postgres.
+ When making a Unix-domain socket connection, if this
+ parameter is set, the client checks at the beginning of the
+ connection that the server process is running under the specified
+ user name; if it is not, the connection is aborted with an error.
+ This parameter can be used to provide server authentication similar
+ to that available with SSL certificates on TCP/IP connections.
+ (Note that if the Unix-domain socket is in
+ /tmp or another publicly writable location,
+ any user could start a server listening there. Use this parameter
+ to ensure that you are connected to a server run by a trusted user.)
+ This option is only supported on platforms for which the
+ peer> authentication method is implemented; see
+ .
+
+
+
+
+
+ krbsrvname
+
+
+ Kerberos service name to use when authenticating with Kerberos 5
+ or GSSAPI.
+ This must match the service name specified in the server
+ configuration for Kerberos authentication to succeed. (See also
+ and .)
+
+
+
+
+
+ gsslib
+
+
+ GSS library to use for GSSAPI authentication. Only used on Windows.
+ Set to gssapi to force libpq to use the GSSAPI
+ library for authentication instead of the default SSPI.
+
+
+
+
+
+ service
+
+
+ Service name to use for additional parameters. It specifies a service
+ name in pg_service.conf that holds additional connection parameters.
+ This allows applications to specify only a service name so connection parameters
+ can be centrally maintained. See .
+
+
+
+
+
+
+
+
+ Connection Strings
+
+
+ conninfo
+
+
+
+ URI
+
+
+
+ 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 URIs.
+
+
+
+ In the first format, each parameter setting is in the form
+ keyword = value. Spaces around the equal sign are
+ optional. To write an empty value, or a value containing spaces, surround it
+ with single quotes, e.g., keyword = 'a value'. Single
+ quotes and backslashes within
+ the value must be escaped with a backslash, i.e., \' and
+ \\.
+
+
+
+ The currently recognized parameter key words are listed in
+ .
+
+
+
+ The general form for connection URI is the
+ following:
+
+postgresql://[user[:password]@][unix-socket][:port[/dbname]][?param1=value1&...]
+postgresql://[user[:password]@][net-location][:port][/dbname][?param1=value1&...]
+
+
+
+
+ The URI designator can be either
+ postgresql:// or postgres:// and
+ each of the URI parts is optional. The following
+ examples illustrate valid URI syntax uses:
+
+postgresql://
+postgresql://localhost
+postgresql://localhost:5433
+postgresql://localhost/mydb
+postgresql://user@localhost
+postgresql://user:secret@localhost
+postgresql://other@localhost/otherdb
+
+
+
+
+ Percent-encoding may be used to include a symbol with special meaning in
+ any of the URI parts.
+
+
+
+ Additional connection parameters may optionally follow the base URI.
+ Any connection parameters not corresponding to key words listed
+ in are ignored and a warning message
+ about them is sent to stderr.
+
+
+
+ For improved compatibility with JDBC connection URI
+ syntax, instances of parameter ssl=true are translated
+ into sslmode=require (see above.)
+
+
+
+ The host part may be either hostname or an IP address. To specify an
+ IPv6 host address, enclose it in square brackets:
+
+postgresql://[2001:db8::1234]/database
+
+ As a special case, a host part which starts with / is
+ treated as a local Unix socket directory to look for the connection
+ socket special file:
+
+postgresql:///path/to/pgsql/socket/dir
+
+ The whole connection string up to the extra parameters designator
+ (?) or the port designator (:) is treated
+ as the absolute path to the socket directory
+ (/path/to/pgsql/socket/dir in this example.) To specify
+ a non-default database name in this case you can use either of the following
+ syntaxes:
+
+postgresql:///path/to/pgsql/socket/dir?dbname=otherdb
+postgresql:///path/to/pgsql/socket/dir:5432/otherdb
+
+
+
+
diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index b8491015f4..bdcadf3692 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -115,7 +115,10 @@ PostgreSQL documentation
argument on the command line.
- If this parameter contains an = sign, it is treated as a
+ If this parameter contains an = sign or starts
+ with a valid URI prefix
+ (postgresql://
+ or postgres://), it is treated as a
conninfo string. See for more information.
@@ -596,11 +599,13 @@ PostgreSQL documentation
An alternative way to specify connection parameters is in a
- conninfo string, which is used instead of a
- database name. This mechanism give you very wide control over the
+ conninfo string or
+ a URI, which is used instead of a database
+ name. This mechanism give you very wide control over the
connection. For example:
$ psql "service=myservice sslmode=require"
+$ psql postgresql://dbmaster:5433/mydb?sslmode=require
This way you can also use LDAP for connection parameter lookup as
described in .
diff --git a/src/interfaces/libpq/Makefile b/src/interfaces/libpq/Makefile
index d1ded1f0e5..ec4fdd403e 100644
--- a/src/interfaces/libpq/Makefile
+++ b/src/interfaces/libpq/Makefile
@@ -121,6 +121,9 @@ install: all installdirs install-lib
$(INSTALL_DATA) $(srcdir)/pqexpbuffer.h '$(DESTDIR)$(includedir_internal)'
$(INSTALL_DATA) $(srcdir)/pg_service.conf.sample '$(DESTDIR)$(datadir)/pg_service.conf.sample'
+installcheck:
+ $(MAKE) -C test $@
+
installdirs: installdirs-lib
$(MKDIR_P) '$(DESTDIR)$(includedir)' '$(DESTDIR)$(includedir_internal)'
@@ -132,6 +135,7 @@ uninstall: uninstall-lib
rm -f '$(DESTDIR)$(datadir)/pg_service.conf.sample'
clean distclean: clean-lib
+ $(MAKE) -C test $@
rm -f $(OBJS) pthread.h libpq.rc
# Might be left over from a Win32 client-only build
rm -f pg_config_paths.h
@@ -142,4 +146,5 @@ clean distclean: clean-lib
rm -f encnames.c wchar.c
maintainer-clean: distclean maintainer-clean-lib
+ $(MAKE) -C test $@
rm -f libpq-dist.rc
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 03fd6e45bb..d0b2ea47cb 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -282,6 +282,9 @@ static const PQEnvironmentOption EnvironmentOptions[] =
}
};
+/* The connection URI must start with either of the following designators: */
+static const char uri_designator[] = "postgresql://";
+static const char short_uri_designator[] = "postgres://";
static bool connectOptions1(PGconn *conn, const char *conninfo);
static bool connectOptions2(PGconn *conn);
@@ -293,6 +296,10 @@ static void fillPGconn(PGconn *conn, PQconninfoOption *connOptions);
static void freePGconn(PGconn *conn);
static void closePGconn(PGconn *conn);
static PQconninfoOption *conninfo_init(PQExpBuffer errorMessage);
+static PQconninfoOption *parse_connection_string(const char *conninfo,
+ PQExpBuffer errorMessage, bool use_defaults);
+static int uri_prefix_length(const char *connstr);
+static bool recognized_connection_string(const char *connstr);
static PQconninfoOption *conninfo_parse(const char *conninfo,
PQExpBuffer errorMessage, bool use_defaults);
static PQconninfoOption *conninfo_array_parse(const char *const * keywords,
@@ -300,8 +307,22 @@ static PQconninfoOption *conninfo_array_parse(const char *const * keywords,
bool use_defaults, int expand_dbname);
static bool conninfo_add_defaults(PQconninfoOption *options,
PQExpBuffer errorMessage);
-static char *conninfo_getval(PQconninfoOption *connOptions,
+static PQconninfoOption *conninfo_uri_parse(const char *uri,
+ PQExpBuffer errorMessage, bool use_defaults);
+static bool conninfo_uri_parse_options(PQconninfoOption *options,
+ const char *uri, PQExpBuffer errorMessage);
+static bool conninfo_uri_parse_params(char *params,
+ PQconninfoOption *connOptions,
+ PQExpBuffer errorMessage);
+static char *conninfo_uri_decode(const char *str, PQExpBuffer errorMessage);
+static bool get_hexdigit(char digit, int *value);
+static const char *conninfo_getval(PQconninfoOption *connOptions,
const char *keyword);
+static PQconninfoOption *conninfo_storeval(PQconninfoOption *connOptions,
+ const char *keyword, const char *value,
+ PQExpBuffer errorMessage, bool ignoreMissing, bool uri_decode);
+static PQconninfoOption *conninfo_find(PQconninfoOption *connOptions,
+ const char *keyword);
static void defaultNoticeReceiver(void *arg, const PGresult *res);
static void defaultNoticeProcessor(void *arg, const char *message);
static int parseServiceInfo(PQconninfoOption *options,
@@ -333,9 +354,9 @@ pgthreadlock_t pg_g_threadlock = default_threadlock;
* to the latter).
*
* If it is desired to connect in a synchronous (blocking) manner, use the
- * function PQconnectdb or PQconnectdbParams. The former accepts a string
- * of option = value pairs which must be parsed; the latter takes two NULL
- * terminated arrays instead.
+ * function PQconnectdb or PQconnectdbParams. The former accepts a string of
+ * option = value pairs (or a URI) which must be parsed; the latter takes two
+ * NULL terminated arrays instead.
*
* To connect in an asynchronous (non-blocking) manner, use the functions
* PQconnectStart or PQconnectStartParams (which differ in the same way as
@@ -406,13 +427,14 @@ PQpingParams(const char *const * keywords,
* establishes a connection to a postgres backend through the postmaster
* using connection information in a string.
*
- * The conninfo string is a white-separated list of
+ * The conninfo string is either a whitespace-separated list of
*
* option = value
*
- * definitions. Value might be a single value containing no whitespaces or
- * a single quoted string. If a single quote should appear anywhere in
- * the value, it must be escaped with a backslash like \'
+ * definitions or a URI (refer to the documentation for details.) Value
+ * might be a single value containing no whitespaces or a single quoted
+ * string. If a single quote should appear anywhere in the value, it must be
+ * escaped with a backslash like \'
*
* Returns a PGconn* which is needed for all subsequent libpq calls, or NULL
* if a memory allocation failed.
@@ -583,7 +605,7 @@ PQconnectStart(const char *conninfo)
static void
fillPGconn(PGconn *conn, PQconninfoOption *connOptions)
{
- char *tmp;
+ const char *tmp;
/*
* Move option values into conn structure
@@ -680,7 +702,7 @@ connectOptions1(PGconn *conn, const char *conninfo)
/*
* Parse the conninfo string
*/
- connOptions = conninfo_parse(conninfo, &conn->errorMessage, true);
+ connOptions = parse_connection_string(conninfo, &conn->errorMessage, true);
if (connOptions == NULL)
{
conn->status = CONNECTION_BAD;
@@ -881,9 +903,10 @@ PQsetdbLogin(const char *pghost, const char *pgport, const char *pgoptions,
return NULL;
/*
- * If the dbName parameter contains '=', assume it's a conninfo string.
+ * If the dbName parameter contains what looks like a connection
+ * string, parse it into conn struct using connectOptions1.
*/
- if (dbName && strchr(dbName, '='))
+ if (dbName && recognized_connection_string(dbName))
{
if (!connectOptions1(conn, dbName))
return conn;
@@ -3764,7 +3787,7 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options,
static int
parseServiceInfo(PQconninfoOption *options, PQExpBuffer errorMessage)
{
- char *service = conninfo_getval(options, "service");
+ const char *service = conninfo_getval(options, "service");
char serviceFile[MAXPGPATH];
char *env;
bool group_found = false;
@@ -3999,7 +4022,7 @@ PQconninfoParse(const char *conninfo, char **errmsg)
initPQExpBuffer(&errorBuf);
if (PQExpBufferDataBroken(errorBuf))
return NULL; /* out of memory already :-( */
- connOptions = conninfo_parse(conninfo, &errorBuf, false);
+ connOptions = parse_connection_string(conninfo, &errorBuf, false);
if (connOptions == NULL && errmsg)
*errmsg = errorBuf.data;
else
@@ -4023,17 +4046,68 @@ conninfo_init(PQExpBuffer errorMessage)
return NULL;
}
memcpy(options, PQconninfoOptions, sizeof(PQconninfoOptions));
+
return options;
}
/*
- * Conninfo parser routine
+ * Connection string parser
*
- * If successful, a malloc'd PQconninfoOption array is returned.
- * If not successful, NULL is returned and an error message is
- * left in errorMessage.
- * Defaults are supplied (from a service file, environment variables, etc)
- * for unspecified options, but only if use_defaults is TRUE.
+ * Returns a malloc'd PQconninfoOption array, if parsing is successful.
+ * Otherwise, NULL is returned and an error message is left in errorMessage.
+ *
+ * If use_defaults is TRUE, default values are filled in (from a service file,
+ * environment variables, etc).
+ */
+static PQconninfoOption *
+parse_connection_string(const char *connstr, PQExpBuffer errorMessage,
+ bool use_defaults)
+{
+ /* Parse as URI if connection string matches URI prefix */
+ if (uri_prefix_length(connstr) != 0)
+ return conninfo_uri_parse(connstr, errorMessage, use_defaults);
+
+ /* Parse as default otherwise */
+ return conninfo_parse(connstr, errorMessage, use_defaults);
+}
+
+/*
+ * Checks if connection string starts with either of the valid URI prefix
+ * designators.
+ *
+ * Returns the URI prefix length, 0 if the string doesn't contain a URI prefix.
+ */
+static int
+uri_prefix_length(const char *connstr)
+{
+ if (strncmp(connstr, uri_designator,
+ sizeof(uri_designator) - 1) == 0)
+ return sizeof(uri_designator) - 1;
+
+ if (strncmp(connstr, short_uri_designator,
+ sizeof(short_uri_designator) - 1) == 0)
+ return sizeof(short_uri_designator) - 1;
+
+ return 0;
+}
+
+/*
+ * Recognized connection string either starts with a valid URI prefix or
+ * contains a "=" in it.
+ *
+ * Must be consistent with parse_connection_string: anything for which this
+ * returns true should at least look like it's parseable by that routine.
+ */
+static bool
+recognized_connection_string(const char *connstr)
+{
+ return uri_prefix_length(connstr) != 0 || strchr(connstr, '=') != NULL;
+}
+
+/*
+ * Subroutine for parse_connection_string
+ *
+ * Deal with a string containing key=value pairs.
*/
static PQconninfoOption *
conninfo_parse(const char *conninfo, PQExpBuffer errorMessage,
@@ -4045,7 +4119,6 @@ conninfo_parse(const char *conninfo, PQExpBuffer errorMessage,
char *cp;
char *cp2;
PQconninfoOption *options;
- PQconninfoOption *option;
/* Make a working copy of PQconninfoOptions */
options = conninfo_init(errorMessage);
@@ -4167,33 +4240,10 @@ conninfo_parse(const char *conninfo, PQExpBuffer errorMessage,
}
/*
- * Now we have the name and the value. Search for the param record.
+ * Now that we have the name and the value, store the record.
*/
- for (option = options; option->keyword != NULL; option++)
+ if (!conninfo_storeval(options, pname, pval, errorMessage, false, false))
{
- if (strcmp(option->keyword, pname) == 0)
- break;
- }
- if (option->keyword == NULL)
- {
- printfPQExpBuffer(errorMessage,
- libpq_gettext("invalid connection option \"%s\"\n"),
- pname);
- PQconninfoFree(options);
- free(buf);
- return NULL;
- }
-
- /*
- * Store the value
- */
- if (option->val)
- free(option->val);
- option->val = strdup(pval);
- if (!option->val)
- {
- printfPQExpBuffer(errorMessage,
- libpq_gettext("out of memory\n"));
PQconninfoFree(options);
free(buf);
return NULL;
@@ -4227,10 +4277,10 @@ conninfo_parse(const char *conninfo, PQExpBuffer errorMessage,
* Defaults are supplied (from a service file, environment variables, etc)
* for unspecified options, but only if use_defaults is TRUE.
*
- * If expand_dbname is non-zero, and the value passed for keyword "dbname"
- * contains an "=", assume it is a conninfo string and process it,
- * overriding any previously processed conflicting keywords. Subsequent
- * keywords will take precedence, however.
+ * If expand_dbname is non-zero, and the value passed for keyword "dbname" is a
+ * connection string (as indicated by recognized_connection_string) then parse
+ * and process it, overriding any previously processed conflicting
+ * keywords. Subsequent keywords will take precedence, however.
*/
static PQconninfoOption *
conninfo_array_parse(const char *const * keywords, const char *const * values,
@@ -4238,13 +4288,13 @@ conninfo_array_parse(const char *const * keywords, const char *const * values,
int expand_dbname)
{
PQconninfoOption *options;
- PQconninfoOption *str_options = NULL;
+ PQconninfoOption *dbname_options = NULL;
PQconninfoOption *option;
int i = 0;
/*
* If expand_dbname is non-zero, check keyword "dbname" to see if val is
- * actually a conninfo string
+ * actually a recognized connection string.
*/
while (expand_dbname && keywords[i])
{
@@ -4252,18 +4302,17 @@ conninfo_array_parse(const char *const * keywords, const char *const * values,
const char *pvalue = values[i];
/* first find "dbname" if any */
- if (strcmp(pname, "dbname") == 0)
+ if (strcmp(pname, "dbname") == 0 && pvalue)
{
- /* next look for "=" in the value */
- if (pvalue && strchr(pvalue, '='))
+ /*
+ * If value is a connection string, parse it, but do not use defaults
+ * here -- those get picked up later. We only want to override for
+ * those parameters actually passed.
+ */
+ if (recognized_connection_string(pvalue))
{
- /*
- * Must be a conninfo string, so parse it, but do not use
- * defaults here -- those get picked up later. We only want to
- * override for those parameters actually passed.
- */
- str_options = conninfo_parse(pvalue, errorMessage, false);
- if (str_options == NULL)
+ dbname_options = parse_connection_string(pvalue, errorMessage, false);
+ if (dbname_options == NULL)
return NULL;
}
break;
@@ -4275,7 +4324,7 @@ conninfo_array_parse(const char *const * keywords, const char *const * values,
options = conninfo_init(errorMessage);
if (options == NULL)
{
- PQconninfoFree(str_options);
+ PQconninfoFree(dbname_options);
return NULL;
}
@@ -4302,20 +4351,20 @@ conninfo_array_parse(const char *const * keywords, const char *const * values,
libpq_gettext("invalid connection option \"%s\"\n"),
pname);
PQconninfoFree(options);
- PQconninfoFree(str_options);
+ PQconninfoFree(dbname_options);
return NULL;
}
/*
* If we are on the dbname parameter, and we have a parsed
- * conninfo string, copy those parameters across, overriding any
- * existing previous settings
+ * connection string, copy those parameters across, overriding any
+ * existing previous settings.
*/
- if (strcmp(pname, "dbname") == 0 && str_options)
+ if (strcmp(pname, "dbname") == 0 && dbname_options)
{
PQconninfoOption *str_option;
- for (str_option = str_options; str_option->keyword != NULL; str_option++)
+ for (str_option = dbname_options; str_option->keyword != NULL; str_option++)
{
if (str_option->val != NULL)
{
@@ -4347,14 +4396,14 @@ conninfo_array_parse(const char *const * keywords, const char *const * values,
printfPQExpBuffer(errorMessage,
libpq_gettext("out of memory\n"));
PQconninfoFree(options);
- PQconninfoFree(str_options);
+ PQconninfoFree(dbname_options);
return NULL;
}
}
}
++i;
}
- PQconninfoFree(str_options);
+ PQconninfoFree(dbname_options);
/*
* Add in defaults if the caller wants that.
@@ -4450,16 +4499,618 @@ conninfo_add_defaults(PQconninfoOption *options, PQExpBuffer errorMessage)
return true;
}
+/*
+ * Subroutine for parse_connection_string
+ *
+ * Deal with a URI connection string.
+ */
+static PQconninfoOption *
+conninfo_uri_parse(const char *uri, PQExpBuffer errorMessage,
+ bool use_defaults)
+{
+ PQconninfoOption *options;
+
+ /* Make a working copy of PQconninfoOptions */
+ options = conninfo_init(errorMessage);
+ if (options == NULL)
+ return NULL;
+
+ if (!conninfo_uri_parse_options(options, uri, errorMessage))
+ {
+ PQconninfoFree(options);
+ return NULL;
+ }
+
+ /*
+ * Add in defaults if the caller wants that.
+ */
+ if (use_defaults)
+ {
+ if (!conninfo_add_defaults(options, errorMessage))
+ {
+ PQconninfoFree(options);
+ return NULL;
+ }
+ }
+
+ return options;
+}
+
+/*
+ * conninfo_uri_parse_options
+ * Actual URI parser.
+ *
+ * If successful, returns true while the options array is filled with parsed
+ * options from the URI.
+ * If not successful, returns false and fills errorMessage accordingly.
+ *
+ * Parses the connection URI string in 'uri' according to the URI syntax:
+ *
+ * postgresql://[user[:pwd]@][unix-socket][:port[/dbname]][?param1=value1&...]
+ * postgresql://[user[:pwd]@][net-location][:port][/dbname][?param1=value1&...]
+ *
+ * "net-location" is a hostname, an IPv4 address, or an IPv6 address surrounded
+ * by literal square brackets. To be recognized as a unix-domain socket, the
+ * value must start with a slash '/'. Note slight inconsistency in that dbname
+ * can always be specified after net-location, but after unix-socket it can only
+ * be specified if there is a port specification.
+ *
+ * Any of those elements might be percent-encoded (%xy).
+ */
+static bool
+conninfo_uri_parse_options(PQconninfoOption *options, const char *uri,
+ PQExpBuffer errorMessage)
+{
+ int prefix_len;
+ char *p;
+ char *buf = strdup(uri); /* need a modifiable copy of the input URI */
+ char *start = buf;
+ char prevchar = '\0';
+ bool retval = false;
+
+ if (buf == NULL)
+ {
+ printfPQExpBuffer(errorMessage,
+ libpq_gettext("out of memory\n"));
+ return false;
+ }
+
+ /* Skip the URI prefix */
+ prefix_len = uri_prefix_length(uri);
+ if (prefix_len == 0)
+ {
+ /* Should never happen */
+ printfPQExpBuffer(errorMessage,
+ libpq_gettext("invalid URI propagated to internal parser routine: \"%s\"\n"),
+ uri);
+ goto cleanup;
+ }
+ start += prefix_len;
+ p = start;
+
+ /* Look ahead for possible user credentials designator */
+ while (*p && *p != '@' && *p != '/')
+ ++p;
+ if (*p == '@')
+ {
+ char *user;
+
+ /*
+ * Found username/password designator, so URI should be of the form
+ * "scheme://user[:password]@[netloc]".
+ */
+ user = start;
+
+ p = user;
+ while (*p != ':' && *p != '@')
+ ++p;
+
+ /* Save last char and cut off at end of user name */
+ prevchar = *p;
+ *p = '\0';
+
+ if (!*user)
+ {
+ printfPQExpBuffer(errorMessage,
+ libpq_gettext("invalid empty username specifier in URI: %s\n"),
+ uri);
+ goto cleanup;
+ }
+ if (!conninfo_storeval(options, "user", user,
+ errorMessage, false, true))
+ goto cleanup;
+
+ if (prevchar == ':')
+ {
+ const char *password = p + 1;
+
+ while (*p != '@')
+ ++p;
+ *p = '\0';
+
+ if (!*password)
+ {
+ printfPQExpBuffer(errorMessage,
+ libpq_gettext("invalid empty password specifier in URI: %s\n"),
+ uri);
+ goto cleanup;
+ }
+
+ if (!conninfo_storeval(options, "password", password,
+ errorMessage, false, true))
+ goto cleanup;
+ }
+
+ /* Advance past end of parsed user name or password token */
+ ++p;
+ }
+ else
+ {
+ /*
+ * No username/password designator found. Reset to start of URI.
+ */
+ p = start;
+ }
+
+ /*
+ * "p" has been incremented past optional URI credential information at
+ * this point and now points at the "netloc" part of the URI.
+ *
+ * Check for local unix socket dir.
+ */
+ if (*p == '/')
+ {
+ const char *socket = p;
+
+ /* Look for possible port specifier or query parameters */
+ while (*p && *p != ':' && *p != '?')
+ ++p;
+ prevchar = *p;
+ *p = '\0';
+
+ if (!conninfo_storeval(options, "host", socket,
+ errorMessage, false, true))
+ goto cleanup;
+ }
+ else
+ {
+ /* Not a unix socket dir: parse as host name or address */
+ const char *host;
+
+ /*
+ *
+ * Look for IPv6 address
+ */
+ if (*p == '[')
+ {
+ 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.
+ */
+ if (*p && *p != ':' && *p != '/' && *p != '?')
+ {
+ printfPQExpBuffer(errorMessage,
+ libpq_gettext("unexpected '%c' at position %d in URI (expecting ':' or '/'): %s\n"),
+ *p, (int) (p - buf + 1), uri);
+ goto cleanup;
+ }
+ }
+ 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;
+ }
+
+ /* Save the hostname terminator before we null it */
+ prevchar = *p;
+ *p = '\0';
+
+ if (!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;
+
+ prevchar = *p;
+ *p = '\0';
+
+ if (!*port)
+ {
+ printfPQExpBuffer(errorMessage,
+ libpq_gettext("missing port specifier in URI: %s\n"),
+ uri);
+ goto cleanup;
+ }
+ if (!conninfo_storeval(options, "port", port,
+ errorMessage, false, true))
+ goto cleanup;
+ }
+
+ if (prevchar && prevchar != '?')
+ {
+ const char *dbname = ++p; /* advance past host terminator */
+
+ /* Look for query parameters */
+ while (*p && *p != '?')
+ ++p;
+
+ prevchar = *p;
+ *p = '\0';
+
+ /*
+ * Avoid setting dbname to an empty string, as it forces the default
+ * value (username) and ignores $PGDATABASE, as opposed to not setting
+ * it at all.
+ */
+ if (*dbname &&
+ !conninfo_storeval(options, "dbname", dbname,
+ errorMessage, false, true))
+ goto cleanup;
+ }
+
+ if (prevchar)
+ {
+ ++p; /* advance past terminator */
+
+ if (!conninfo_uri_parse_params(p, options, errorMessage))
+ goto cleanup;
+ }
+
+ /* everything parsed okay */
+ retval = true;
+
+cleanup:
+ free(buf);
+ return retval;
+}
+
+/*
+ * Connection URI parameters parser routine
+ *
+ * If successful, returns true while connOptions is filled with parsed
+ * parameters. Otherwise, returns false and fills errorMessage appropriately.
+ *
+ * Destructively modifies 'params' buffer.
+ */
+static bool
+conninfo_uri_parse_params(char *params,
+ PQconninfoOption *connOptions,
+ PQExpBuffer errorMessage)
+{
+ while (*params)
+ {
+ const char *keyword = params;
+ const char *value = NULL;
+ char *p = params;
+
+ /*
+ * Scan the params string for '=' and '&', marking the end of keyword
+ * and value respectively.
+ */
+ for (;;)
+ {
+ if (*p == '=')
+ {
+ /* Was there '=' already? */
+ if (value != NULL)
+ {
+ printfPQExpBuffer(errorMessage,
+ libpq_gettext("extra key/value separator '=' in URI query parameter: %s\n"),
+ params);
+ return false;
+ }
+ /* Cut off keyword, advance to value */
+ *p = '\0';
+ value = ++p;
+ }
+ else if (*p == '&' || *p == '\0')
+ {
+ char prevchar;
+
+ /* Cut off value, remember old value */
+ prevchar = *p;
+ *p = '\0';
+
+ /* Was there '=' at all? */
+ if (value == NULL)
+ {
+ printfPQExpBuffer(errorMessage,
+ libpq_gettext("missing key/value separator '=' in URI query parameter: %s\n"),
+ params);
+ return false;
+ }
+ /*
+ * If not at the end, advance; now pointing to start of the
+ * next parameter, if any.
+ */
+ if (prevchar != '\0')
+ ++p;
+ break;
+ }
+
+ /* Advance, NUL is checked in the 'if' above */
+ ++p;
+ }
+
+ /*
+ * Special keyword handling for improved JDBC compatibility. Note
+ * we fail to detect URI-encoded values here, but we don't care.
+ */
+ if (strcmp(keyword, "ssl") == 0 &&
+ strcmp(value, "true") == 0)
+ {
+ keyword = "sslmode";
+ value = "require";
+ }
+
+ /*
+ * Store the value if the corresponding option exists; ignore
+ * otherwise.
+ */
+ if (!conninfo_storeval(connOptions, keyword, value,
+ errorMessage, true, true))
+ {
+ /*
+ * Check if there was a hard error when decoding or storing the
+ * option.
+ */
+ if (errorMessage->len != 0)
+ return false;
+
+ fprintf(stderr,
+ libpq_gettext("WARNING: ignoring unrecognized URI query parameter: %s\n"),
+ keyword);
+ }
+
+ /* Proceed to next key=value pair */
+ params = p;
+ }
+
+ return true;
+}
+
+/*
+ * Connection URI decoder routine
+ *
+ * If successful, returns the malloc'd decoded string.
+ * If not successful, returns NULL and fills errorMessage accordingly.
+ *
+ * The string is decoded by replacing any percent-encoded tokens with
+ * corresponding characters, while preserving any non-encoded characters. A
+ * percent-encoded token is a character triplet: a percent sign, followed by a
+ * pair of hexadecimal digits (0-9A-F), where lower- and upper-case letters are
+ * treated identically.
+ */
static char *
+conninfo_uri_decode(const char *str, PQExpBuffer errorMessage)
+{
+ char *buf = malloc(strlen(str) + 1);
+ char *p = buf;
+ const char *q = str;
+
+ if (buf == NULL)
+ {
+ printfPQExpBuffer(errorMessage, libpq_gettext("out of memory\n"));
+ return NULL;
+ }
+
+ for (;;)
+ {
+ if (*q != '%')
+ {
+ /* copy and check for NUL terminator */
+ if (!(*(p++) = *(q++)))
+ break;
+ }
+ else
+ {
+ int hi;
+ int lo;
+ int c;
+
+ ++q; /* skip the percent sign itself */
+
+ /*
+ * Possible EOL will be caught by the first call to get_hexdigit(),
+ * so we never dereference an invalid q pointer.
+ */
+ if (!(get_hexdigit(*q++, &hi) && get_hexdigit(*q++, &lo)))
+ {
+ printfPQExpBuffer(errorMessage,
+ libpq_gettext("invalid percent-encoded token: %s\n"),
+ str);
+ free(buf);
+ return NULL;
+ }
+
+ c = (hi << 4) | lo;
+ if (c == 0)
+ {
+ printfPQExpBuffer(errorMessage,
+ libpq_gettext("forbidden value %%00 in percent-encoded value: %s\n"),
+ str);
+ free(buf);
+ return NULL;
+ }
+ *(p++) = c;
+ }
+ }
+
+ return buf;
+}
+
+/*
+ * Convert hexadecimal digit character to its integer value.
+ *
+ * If successful, returns true and value is filled with digit's base 16 value.
+ * If not successful, returns false.
+ *
+ * Lower- and upper-case letters in the range A-F are treated identically.
+ */
+static bool
+get_hexdigit(char digit, int *value)
+{
+ if ('0' <= digit && digit <= '9')
+ *value = digit - '0';
+ else if ('A' <= digit && digit <= 'F')
+ *value = digit - 'A' + 10;
+ else if ('a' <= digit && digit <= 'f')
+ *value = digit - 'a' + 10;
+ else
+ return false;
+
+ return true;
+}
+
+/*
+ * Find an option value corresponding to the keyword in the connOptions array.
+ *
+ * If successful, returns a pointer to the corresponding option's value.
+ * If not successful, returns NULL.
+ */
+static const char *
conninfo_getval(PQconninfoOption *connOptions,
const char *keyword)
{
PQconninfoOption *option;
+ option = conninfo_find(connOptions, keyword);
+
+ return option ? option->val : NULL;
+}
+
+/*
+ * Store a (new) value for an option corresponding to the keyword in
+ * connOptions array.
+ *
+ * If uri_decode is true, keyword and value are URI-decoded.
+ *
+ * If successful, returns a pointer to the corresponding PQconninfoOption,
+ * which value is replaced with a strdup'd copy of the passed value string.
+ * The existing value for the option is free'd before replacing, if any.
+ *
+ * If not successful, returns NULL and fills errorMessage accordingly.
+ * However, if the reason of failure is an invalid keyword being passed and
+ * ignoreMissing is TRUE, errorMessage will be left untouched.
+ */
+static PQconninfoOption *
+conninfo_storeval(PQconninfoOption *connOptions,
+ const char *keyword, const char *value,
+ PQExpBuffer errorMessage, bool ignoreMissing,
+ bool uri_decode)
+{
+ PQconninfoOption *option;
+ char *value_copy;
+ char *keyword_copy = NULL;
+
+ /*
+ * Decode the keyword. XXX this is seldom necessary as keywords do not
+ * normally need URI-escaping. It'd be good to do away with the
+ * malloc/free overhead and the general ugliness, but I don't see a
+ * better way to handle it.
+ */
+ if (uri_decode)
+ {
+ keyword_copy = conninfo_uri_decode(keyword, errorMessage);
+ if (keyword_copy == NULL)
+ /* conninfo_uri_decode already set an error message */
+ goto failed;
+ }
+
+ option = conninfo_find(connOptions,
+ keyword_copy != NULL ? keyword_copy : keyword);
+ if (option == NULL)
+ {
+ if (!ignoreMissing)
+ printfPQExpBuffer(errorMessage,
+ libpq_gettext("invalid connection option \"%s\"\n"),
+ keyword);
+ goto failed;
+ }
+
+ if (uri_decode)
+ {
+ value_copy = conninfo_uri_decode(value, errorMessage);
+ if (value_copy == NULL)
+ /* conninfo_uri_decode already set an error message */
+ goto failed;
+ }
+ else
+ {
+ value_copy = strdup(value);
+
+ if (value_copy == NULL)
+ {
+ printfPQExpBuffer(errorMessage, libpq_gettext("out of memory\n"));
+ goto failed;
+ }
+ }
+
+ if (option->val)
+ free(option->val);
+ option->val = value_copy;
+
+ if (keyword_copy != NULL)
+ free(keyword_copy);
+ return option;
+
+failed:
+ if (keyword_copy != NULL)
+ free(keyword_copy);
+ return NULL;
+}
+
+/*
+ * Find a PQconninfoOption option corresponding to the keyword in the
+ * connOptions array.
+ *
+ * If successful, returns a pointer to the corresponding PQconninfoOption
+ * structure.
+ * If not successful, returns NULL.
+ */
+static PQconninfoOption *
+conninfo_find(PQconninfoOption *connOptions, const char *keyword)
+{
+ PQconninfoOption *option;
+
for (option = connOptions; option->keyword != NULL; option++)
{
if (strcmp(option->keyword, keyword) == 0)
- return option->val;
+ return option;
}
return NULL;
diff --git a/src/interfaces/libpq/test/Makefile b/src/interfaces/libpq/test/Makefile
new file mode 100644
index 0000000000..b9023c37f3
--- /dev/null
+++ b/src/interfaces/libpq/test/Makefile
@@ -0,0 +1,22 @@
+subdir = src/interfaces/libpq/test
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+
+ifeq ($(PORTNAME), win32)
+LDLIBS += -lws2_32
+endif
+
+override CPPFLAGS := -I$(libpq_srcdir) $(CPPFLAGS)
+override LDLIBS := $(libpq_pgport) $(LDLIBS)
+
+PROGS = uri-regress
+
+all: $(PROGS)
+
+installcheck: all
+ SRCDIR='$(top_srcdir)' SUBDIR='$(subdir)' \
+ $(SHELL) $(top_srcdir)/$(subdir)/regress.sh
+
+clean distclean maintainer-clean:
+ rm -f $(PROGS)
+ rm -f regress.out regress.diff
diff --git a/src/interfaces/libpq/test/README b/src/interfaces/libpq/test/README
new file mode 100644
index 0000000000..001ecc378d
--- /dev/null
+++ b/src/interfaces/libpq/test/README
@@ -0,0 +1,7 @@
+This is a testsuite for testing libpq URI connection string syntax.
+
+To run the suite, use 'make installcheck' command. It works by
+running 'regress.sh' from this directory with appropriate environment
+set up, which in turn feeds up lines from 'regress.in' to
+'uri-regress' test program and compares the output against the correct
+one in 'expected.out' file.
diff --git a/src/interfaces/libpq/test/expected.out b/src/interfaces/libpq/test/expected.out
new file mode 100644
index 0000000000..54a6291bc4
--- /dev/null
+++ b/src/interfaces/libpq/test/expected.out
@@ -0,0 +1,163 @@
+trying postgresql://uri-user:secret@host:12345/db
+user='uri-user' password='secret' dbname='db' host='host' port='12345' (inet)
+
+trying postgresql://uri-user@host:12345/db
+user='uri-user' dbname='db' host='host' port='12345' (inet)
+
+trying postgresql://uri-user@host/db
+user='uri-user' dbname='db' host='host' (inet)
+
+trying postgresql://host:12345/db
+dbname='db' host='host' port='12345' (inet)
+
+trying postgresql://host/db
+dbname='db' host='host' (inet)
+
+trying postgresql://uri-user@host:12345/
+user='uri-user' host='host' port='12345' (inet)
+
+trying postgresql://uri-user@host/
+user='uri-user' host='host' (inet)
+
+trying postgresql://uri-user@
+user='uri-user' host='' (local)
+
+trying postgresql://host:12345/
+host='host' port='12345' (inet)
+
+trying postgresql://host:12345
+host='host' port='12345' (inet)
+
+trying postgresql://host/db
+dbname='db' host='host' (inet)
+
+trying postgresql://host/
+host='host' (inet)
+
+trying postgresql://host
+host='host' (inet)
+
+trying postgresql://
+host='' (local)
+
+trying postgresql://?hostaddr=127.0.0.1
+host='' hostaddr='127.0.0.1' (inet)
+
+trying postgresql://example.com?hostaddr=63.1.2.4
+host='example.com' hostaddr='63.1.2.4' (inet)
+
+trying postgresql://%68ost/
+host='host' (inet)
+
+trying postgresql://host/db?user=uri-user
+user='uri-user' dbname='db' host='host' (inet)
+
+trying postgresql://host/db?user=uri-user&port=12345
+user='uri-user' dbname='db' host='host' port='12345' (inet)
+
+trying postgresql://host/db?u%73er=someotheruser&port=12345
+user='someotheruser' dbname='db' host='host' port='12345' (inet)
+
+trying postgresql://host/db?u%7aer=someotheruser&port=12345
+WARNING: ignoring unrecognized URI query parameter: u%7aer
+dbname='db' host='host' port='12345' (inet)
+
+trying postgresql://host:12345?user=uri-user
+user='uri-user' host='host' port='12345' (inet)
+
+trying postgresql://host?user=uri-user
+user='uri-user' host='host' (inet)
+
+trying postgresql://host?
+host='host' (inet)
+
+trying postgresql://[::1]:12345/db
+dbname='db' host='::1' port='12345' (inet)
+
+trying postgresql://[::1]/db
+dbname='db' host='::1' (inet)
+
+trying postgresql://[2001:db8::1234]/
+host='2001:db8::1234' (inet)
+
+trying postgresql://[200z:db8::1234]/
+host='200z:db8::1234' (inet)
+
+trying postgresql://[::1]
+host='::1' (inet)
+
+trying postgres://
+host='' (local)
+
+trying postgres:///tmp
+host='/tmp' (local)
+
+trying postgresql://host?uzer=
+WARNING: ignoring unrecognized URI query parameter: uzer
+host='host' (inet)
+
+trying postgre://
+uri-regress: missing "=" after "postgre://" in connection info string
+
+
+trying postgres://[::1
+uri-regress: end of string reached when looking for matching ']' in IPv6 host address in URI: postgres://[::1
+
+
+trying postgres://[]
+uri-regress: IPv6 host address may not be empty in URI: postgres://[]
+
+
+trying postgres://[::1]z
+uri-regress: unexpected 'z' at position 17 in URI (expecting ':' or '/'): postgres://[::1]z
+
+
+trying postgresql://host?zzz
+uri-regress: missing key/value separator '=' in URI query parameter: zzz
+
+
+trying postgresql://host?value1&value2
+uri-regress: missing key/value separator '=' in URI query parameter: value1
+
+
+trying postgresql://host?key=key=value
+uri-regress: extra key/value separator '=' in URI query parameter: key
+
+
+trying postgres://host?dbname=%XXfoo
+uri-regress: invalid percent-encoded token: %XXfoo
+
+
+trying postgresql://a%00b
+uri-regress: forbidden value %00 in percent-encoded value: a%00b
+
+
+trying postgresql://%zz
+uri-regress: invalid percent-encoded token: %zz
+
+
+trying postgresql://%1
+uri-regress: invalid percent-encoded token: %1
+
+
+trying postgresql://%
+uri-regress: invalid percent-encoded token: %
+
+
+trying postgres://@host
+uri-regress: invalid empty username specifier in URI: postgres://@host
+
+
+trying postgres://host:/
+uri-regress: missing port specifier in URI: postgres://host:/
+
+
+trying postgres://otheruser@/no/such/directory
+user='otheruser' host='/no/such/directory' (local)
+
+trying postgres://otheruser@/no/such/socket/path:12345
+user='otheruser' host='/no/such/socket/path' port='12345' (local)
+
+trying postgres://otheruser@/path/to/socket:12345/db
+user='otheruser' dbname='db' host='/path/to/socket' port='12345' (local)
+
diff --git a/src/interfaces/libpq/test/regress.in b/src/interfaces/libpq/test/regress.in
new file mode 100644
index 0000000000..8d33ae1ac1
--- /dev/null
+++ b/src/interfaces/libpq/test/regress.in
@@ -0,0 +1,49 @@
+postgresql://uri-user:secret@host:12345/db
+postgresql://uri-user@host:12345/db
+postgresql://uri-user@host/db
+postgresql://host:12345/db
+postgresql://host/db
+postgresql://uri-user@host:12345/
+postgresql://uri-user@host/
+postgresql://uri-user@
+postgresql://host:12345/
+postgresql://host:12345
+postgresql://host/db
+postgresql://host/
+postgresql://host
+postgresql://
+postgresql://?hostaddr=127.0.0.1
+postgresql://example.com?hostaddr=63.1.2.4
+postgresql://%68ost/
+postgresql://host/db?user=uri-user
+postgresql://host/db?user=uri-user&port=12345
+postgresql://host/db?u%73er=someotheruser&port=12345
+postgresql://host/db?u%7aer=someotheruser&port=12345
+postgresql://host:12345?user=uri-user
+postgresql://host?user=uri-user
+postgresql://host?
+postgresql://[::1]:12345/db
+postgresql://[::1]/db
+postgresql://[2001:db8::1234]/
+postgresql://[200z:db8::1234]/
+postgresql://[::1]
+postgres://
+postgres:///tmp
+postgresql://host?uzer=
+postgre://
+postgres://[::1
+postgres://[]
+postgres://[::1]z
+postgresql://host?zzz
+postgresql://host?value1&value2
+postgresql://host?key=key=value
+postgres://host?dbname=%XXfoo
+postgresql://a%00b
+postgresql://%zz
+postgresql://%1
+postgresql://%
+postgres://@host
+postgres://host:/
+postgres://otheruser@/no/such/directory
+postgres://otheruser@/no/such/socket/path:12345
+postgres://otheruser@/path/to/socket:12345/db
diff --git a/src/interfaces/libpq/test/regress.sh b/src/interfaces/libpq/test/regress.sh
new file mode 100644
index 0000000000..298d8bdc4a
--- /dev/null
+++ b/src/interfaces/libpq/test/regress.sh
@@ -0,0 +1,21 @@
+#!/bin/sh
+
+while read line
+do
+ echo "trying $line"
+ ./uri-regress "$line"
+ echo ""
+done < "${SRCDIR}/${SUBDIR}"/regress.in >regress.out 2>&1
+
+if diff -c "${SRCDIR}/${SUBDIR}/"expected.out regress.out >regress.diff; then
+ echo "========================================"
+ echo "All tests passed"
+ exit 0
+else
+ echo "========================================"
+ echo "FAILED: the test result differs from the expected output"
+ echo
+ echo "Review the difference in ${SUBDIR}/regress.diff"
+ echo "========================================"
+ exit 1
+fi
diff --git a/src/interfaces/libpq/test/uri-regress.c b/src/interfaces/libpq/test/uri-regress.c
new file mode 100644
index 0000000000..17fcce9fb2
--- /dev/null
+++ b/src/interfaces/libpq/test/uri-regress.c
@@ -0,0 +1,84 @@
+/*
+ * uri-regress.c
+ * A test program for libpq URI format
+ *
+ * This is a helper for libpq conninfo regression testing. It takes a single
+ * conninfo string as a parameter, parses it using PQconninfoParse, and then
+ * prints out the values from the parsed PQconninfoOption struct that differ
+ * from the defaults (obtained from PQconndefaults).
+ *
+ * Portions Copyright (c) 2012, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ * src/interfaces/libpq/test/uri-regress.c
+ */
+
+#include "postgres_fe.h"
+
+#include "libpq-fe.h"
+
+int
+main(int argc, char *argv[])
+{
+ PQconninfoOption *opts;
+ PQconninfoOption *defs;
+ PQconninfoOption *opt;
+ PQconninfoOption *def;
+ char *errmsg = NULL;
+ bool local = true;
+
+ if (argc != 2)
+ return 1;
+
+ opts = PQconninfoParse(argv[1], &errmsg);
+ if (opts == NULL)
+ {
+ fprintf(stderr, "uri-regress: %s\n", errmsg);
+ return 1;
+ }
+
+ defs = PQconndefaults();
+ if (defs == NULL)
+ {
+ fprintf(stderr, "uri-regress: cannot fetch default options\n");
+ return 1;
+ }
+
+ /*
+ * Loop on the options, and print the value of each if not the default.
+ *
+ * XXX this coding assumes that PQconninfoOption structs always have the
+ * keywords in the same order.
+ */
+ for (opt = opts, def = defs; opt->keyword; ++opt, ++def)
+ {
+ if (opt->val != NULL)
+ {
+ if (def->val == NULL || strcmp(opt->val, def->val) != 0)
+ printf("%s='%s' ", opt->keyword, opt->val);
+
+ /*
+ * Try to detect if this is a Unix-domain socket or inet. This is
+ * a bit grotty but it's the same thing that libpq itself does.
+ *
+ * Note that we directly test for '/' instead of using
+ * is_absolute_path, as that would be considerably more messy.
+ * This would fail on Windows, but that platform doesn't have
+ * Unix-domain sockets anyway.
+ */
+ if (*opt->val &&
+ (strcmp(opt->keyword, "hostaddr") == 0 ||
+ (strcmp(opt->keyword, "host") == 0 && *opt->val != '/')))
+ {
+ local = false;
+ }
+ }
+ }
+
+ if (local)
+ printf("(local)\n");
+ else
+ printf("(inet)\n");
+
+ return 0;
+}