diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index 0ec501e5bda8cea44856747bbd91e4590f50cc30..8a820ac0074f63cf7d48f117000b73137ff57a02 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -116,10 +116,16 @@ PGconn *PQconnectdbParams(const char * const *keywords,
        programming.
       </para>
 
+      <para>
+       The currently recognized parameter key words are listed in
+       <xref linkend="libpq-paramkeywords">.
+      </para>
+
       <para>
        When <literal>expand_dbname</literal> is non-zero, the
        <parameter>dbname</parameter> key word value is allowed to be recognized
-       as a <parameter>conninfo</parameter> string. See below for details.
+       as a connection string. More details on the possible formats appear in
+       <xref linkend="libpq-connstring">.
       </para>
 
       <para>
@@ -130,507 +136,6 @@ PGconn *PQconnectdbParams(const char * const *keywords,
       </para>
 
       <para>
-       The currently recognized parameter key words are:
-
-       <variablelist>
-        <varlistentry id="libpq-connect-host" xreflabel="host">
-         <term><literal>host</literal></term>
-         <listitem>
-          <para>
-           Name of host to connect to.<indexterm><primary>host name</></>
-           If this begins with a slash, it specifies Unix-domain
-           communication rather than TCP/IP communication; the value is the
-           name of the directory in which the socket file is stored.  The
-           default behavior when <literal>host</literal> is not specified
-           is to connect to a Unix-domain
-           socket<indexterm><primary>Unix domain socket</></> in
-           <filename>/tmp</filename> (or whatever socket directory was specified
-           when <productname>PostgreSQL</> was built). On machines without
-           Unix-domain sockets, the default is to connect to <literal>localhost</>.
-          </para>
-         </listitem>
-        </varlistentry>
-
-        <varlistentry id="libpq-connect-hostaddr" xreflabel="hostaddr">
-         <term><literal>hostaddr</literal></term>
-         <listitem>
-          <para>
-           Numeric IP address of host to connect to.  This should be in the
-           standard IPv4 address format, e.g., <literal>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.
-          </para>
-
-          <para>
-           Using <literal>hostaddr</> instead of <literal>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 <literal>verify-full</> SSL
-           certificate verification.  The following rules are used:
-           <itemizedlist>
-            <listitem>
-             <para>
-              If <literal>host</> is specified without <literal>hostaddr</>,
-              a host name lookup occurs.
-             </para>
-            </listitem>
-            <listitem>
-             <para>
-              If <literal>hostaddr</> is specified without <literal>host</>,
-              the value for <literal>hostaddr</> gives the server network address.
-              The connection attempt will fail if the authentication
-              method requires a host name.
-             </para>
-            </listitem>
-            <listitem>
-             <para>
-              If both <literal>host</> and <literal>hostaddr</> are specified,
-              the value for <literal>hostaddr</> gives the server network address.
-              The value for <literal>host</> is ignored unless the
-              authentication method requires it, in which case it will be
-              used as the host name.
-             </para>
-            </listitem>
-           </itemizedlist>
-           Note that authentication is likely to fail if <literal>host</>
-           is not the name of the server at network address <literal>hostaddr</>.
-           Also, note that <literal>host</> rather than <literal>hostaddr</>
-           is used to identify the connection in <filename>~/.pgpass</> (see
-           <xref linkend="libpq-pgpass">).
-          </para>
-
-          <para>
-           Without either a host name or host address,
-           <application>libpq</application> will connect using a
-           local Unix-domain socket; or on machines without Unix-domain
-           sockets, it will attempt to connect to <literal>localhost</>.
-          </para>
-          </listitem>
-         </varlistentry>
-
-         <varlistentry id="libpq-connect-port" xreflabel="port">
-          <term><literal>port</literal></term>
-          <listitem>
-          <para>
-           Port number to connect to at the server host, or socket file
-           name extension for Unix-domain
-           connections.<indexterm><primary>port</></>
-          </para>
-         </listitem>
-        </varlistentry>
-
-        <varlistentry id="libpq-connect-dbname" xreflabel="dbname">
-         <term><literal>dbname</literal></term>
-         <listitem>
-         <para>
-          The database name.  Defaults to be the same as the user name.
-         </para>
-         </listitem>
-        </varlistentry>
-
-        <varlistentry id="libpq-connect-user" xreflabel="user">
-         <term><literal>user</literal></term>
-         <listitem>
-         <para>
-          <productname>PostgreSQL</productname> user name to connect as.
-          Defaults to be the same as the operating system name of the user
-          running the application.
-         </para>
-         </listitem>
-        </varlistentry>
-
-        <varlistentry id="libpq-connect-password" xreflabel="password">
-         <term><literal>password</literal></term>
-         <listitem>
-         <para>
-          Password to be used if the server demands password authentication.
-         </para>
-         </listitem>
-        </varlistentry>
-
-        <varlistentry id="libpq-connect-connect-timeout" xreflabel="connect_timeout">
-         <term><literal>connect_timeout</literal></term>
-         <listitem>
-         <para>
-          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.
-         </para>
-         </listitem>
-        </varlistentry>
-
-        <varlistentry id="libpq-connect-client-encoding" xreflabel="client_encoding">
-         <term><literal>client_encoding</literal></term>
-         <listitem>
-         <para>
-          This sets the <varname>client_encoding</varname>
-          configuration parameter for this connection.  In addition to
-          the values accepted by the corresponding server option, you
-          can use <literal>auto</literal> to determine the right
-          encoding from the current locale in the client
-          (<envar>LC_CTYPE</envar> environment variable on Unix
-          systems).
-         </para>
-         </listitem>
-        </varlistentry>
-
-        <varlistentry id="libpq-connect-options" xreflabel="options">
-         <term><literal>options</literal></term>
-         <listitem>
-          <para>
-           Adds command-line options to send to the server at run-time.
-           For example, setting this to <literal>-c geqo=off</> sets the
-           session's value of the <varname>geqo</> parameter to
-           <literal>off</>.  For a detailed discussion of the available
-           options, consult <xref linkend="runtime-config">.
-          </para>
-         </listitem>
-        </varlistentry>
-
-        <varlistentry id="libpq-connect-application-name" xreflabel="application_name">
-         <term><literal>application_name</literal></term>
-         <listitem>
-          <para>
-           Specifies a value for the <xref linkend="guc-application-name">
-           configuration parameter.
-          </para>
-         </listitem>
-        </varlistentry>
-
-        <varlistentry id="libpq-connect-fallback-application-name" xreflabel="fallback_application_name">
-         <term><literal>fallback_application_name</literal></term>
-         <listitem>
-          <para>
-           Specifies a fallback value for the <xref
-           linkend="guc-application-name"> configuration parameter.
-           This value will be used if no value has been given for
-           <literal>application_name</> via a connection parameter or the
-           <envar>PGAPPNAME</envar> 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.
-          </para>
-         </listitem>
-        </varlistentry>
-
-        <varlistentry id="libpq-keepalives" xreflabel="keepalives">
-         <term><literal>keepalives</literal></term>
-         <listitem>
-          <para>
-           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.
-          </para>
-         </listitem>
-        </varlistentry>
-
-        <varlistentry id="libpq-keepalives-idle" xreflabel="keepalives_idle">
-         <term><literal>keepalives_idle</literal></term>
-         <listitem>
-          <para>
-           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 <symbol>TCP_KEEPIDLE</> or <symbol>TCP_KEEPALIVE</>
-           socket option is available, and on Windows; on other systems, it has no
-           effect.
-          </para>
-         </listitem>
-        </varlistentry>
-
-        <varlistentry id="libpq-keepalives-interval" xreflabel="keepalives_interval">
-         <term><literal>keepalives_interval</literal></term>
-         <listitem>
-          <para>
-           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 <symbol>TCP_KEEPINTVL</>
-           socket option is available, and on Windows; on other systems, it has no
-           effect.
-          </para>
-         </listitem>
-        </varlistentry>
-
-        <varlistentry id="libpq-keepalives-count" xreflabel="keepalives_count">
-         <term><literal>keepalives_count</literal></term>
-         <listitem>
-          <para>
-           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 <symbol>TCP_KEEPCNT</>
-           socket option is available; on other systems, it has no effect.
-          </para>
-         </listitem>
-        </varlistentry>
-
-        <varlistentry id="libpq-connect-tty" xreflabel="tty">
-         <term><literal>tty</literal></term>
-         <listitem>
-         <para>
-          Ignored (formerly, this specified where to send server debug output).
-         </para>
-         </listitem>
-        </varlistentry>
-
-        <varlistentry id="libpq-connect-sslmode" xreflabel="sslmode">
-         <term><literal>sslmode</literal></term>
-         <listitem>
-          <para>
-           This option determines whether or with what priority a secure
-           <acronym>SSL</> TCP/IP connection will be negotiated with the
-           server. There are six modes:
-
-           <variablelist>
-            <varlistentry>
-             <term><literal>disable</literal></term>
-             <listitem>
-              <para>
-               only try a non-<acronym>SSL</> connection
-              </para>
-             </listitem>
-            </varlistentry>
-
-            <varlistentry>
-             <term><literal>allow</literal></term>
-             <listitem>
-              <para>
-               first try a non-<acronym>SSL</> connection; if that
-               fails, try an <acronym>SSL</> connection
-              </para>
-             </listitem>
-            </varlistentry>
-
-            <varlistentry>
-             <term><literal>prefer</literal> (default)</term>
-             <listitem>
-              <para>
-               first try an <acronym>SSL</> connection; if that fails,
-               try a non-<acronym>SSL</> connection
-              </para>
-             </listitem>
-            </varlistentry>
-
-            <varlistentry>
-             <term><literal>require</literal></term>
-             <listitem>
-              <para>
-               only try an <acronym>SSL</> connection. If a root CA
-               file is present, verify the certificate in the same way as
-               if <literal>verify-ca</literal> was specified
-              </para>
-             </listitem>
-            </varlistentry>
-
-            <varlistentry>
-             <term><literal>verify-ca</literal></term>
-             <listitem>
-              <para>
-               only try an <acronym>SSL</> connection, and verify that
-               the server certificate is issued by a trusted
-               certificate authority (<acronym>CA</>)
-              </para>
-             </listitem>
-            </varlistentry>
-
-            <varlistentry>
-             <term><literal>verify-full</literal></term>
-             <listitem>
-              <para>
-               only try an <acronym>SSL</> connection, verify that the
-               server certificate is issued by a
-               trusted <acronym>CA</> and that the server host name
-               matches that in the certificate
-              </para>
-             </listitem>
-            </varlistentry>
-           </variablelist>
-
-           See <xref linkend="libpq-ssl"> for a detailed description of how
-           these options work.
-          </para>
-
-          <para>
-           <literal>sslmode</> is ignored for Unix domain socket
-           communication.
-           If <productname>PostgreSQL</> is compiled without SSL support,
-           using options <literal>require</>, <literal>verify-ca</>, or
-           <literal>verify-full</> will cause an error, while
-           options <literal>allow</> and <literal>prefer</> will be
-           accepted but <application>libpq</> will not actually attempt
-           an <acronym>SSL</>
-           connection.<indexterm><primary>SSL</><secondary
-           sortas="libpq">with libpq</></indexterm>
-          </para>
-         </listitem>
-        </varlistentry>
-
-        <varlistentry id="libpq-connect-requiressl" xreflabel="requiressl">
-         <term><literal>requiressl</literal></term>
-         <listitem>
-          <para>
-           This option is deprecated in favor of the <literal>sslmode</>
-           setting.
-          </para>
-
-          <para>
-           If set to 1, an <acronym>SSL</acronym> connection to the server
-           is required (this is equivalent to <literal>sslmode</>
-           <literal>require</>).  <application>libpq</> will then refuse
-           to connect if the server does not accept an
-           <acronym>SSL</acronym> connection.  If set to 0 (default),
-           <application>libpq</> will negotiate the connection type with
-           the server (equivalent to <literal>sslmode</>
-           <literal>prefer</>).  This option is only available if
-           <productname>PostgreSQL</> is compiled with SSL support.
-          </para>
-         </listitem>
-        </varlistentry>
-
-        <varlistentry id="libpq-connect-sslcompression" xreflabel="sslcompression">
-         <term><literal>sslcompression</literal></term>
-         <listitem>
-          <para>
-           If set to 1 (default), data sent over SSL connections will be
-           compressed (this requires <productname>OpenSSL</> version
-           0.9.8 or later).
-           If set to 0, compression will be disabled (this requires
-           <productname>OpenSSL</> 1.0.0 or later).
-           This parameter is ignored if a connection without SSL is made,
-           or if the version of <productname>OpenSSL</> used does not support
-           it.
-          </para>
-          <para>
-           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.
-          </para>
-         </listitem>
-        </varlistentry>
-
-        <varlistentry id="libpq-connect-sslcert" xreflabel="sslcert">
-         <term><literal>sslcert</literal></term>
-         <listitem>
-          <para>
-           This parameter specifies the file name of the client SSL
-           certificate, replacing the default
-           <filename>~/.postgresql/postgresql.crt</>.
-           This parameter is ignored if an SSL connection is not made.
-          </para>
-         </listitem>
-        </varlistentry>
-
-        <varlistentry id="libpq-connect-sslkey" xreflabel="sslkey">
-         <term><literal>sslkey</literal></term>
-         <listitem>
-          <para>
-           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
-           <filename>~/.postgresql/postgresql.key</>, or it can specify a key
-           obtained from an external <quote>engine</> (engines are
-           <productname>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.
-          </para>
-         </listitem>
-        </varlistentry>
-
-        <varlistentry id="libpq-connect-sslrootcert" xreflabel="sslrootcert">
-         <term><literal>sslrootcert</literal></term>
-         <listitem>
-          <para>
-           This parameter specifies the name of a file containing SSL
-           certificate authority (<acronym>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
-           <filename>~/.postgresql/root.crt</>.
-          </para>
-         </listitem>
-        </varlistentry>
-
-        <varlistentry id="libpq-connect-sslcrl" xreflabel="sslcrl">
-         <term><literal>sslcrl</literal></term>
-         <listitem>
-          <para>
-           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
-           <filename>~/.postgresql/root.crl</>.
-          </para>
-         </listitem>
-        </varlistentry>
-
-        <varlistentry id="libpq-connect-requirepeer" xreflabel="requirepeer">
-         <term><literal>requirepeer</literal></term>
-         <listitem>
-          <para>
-           This parameter specifies the operating-system user name of the
-           server, for example <literal>requirepeer=postgres</literal>.
-           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
-           <filename>/tmp</filename> 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
-           <literal>peer</> authentication method is implemented; see
-           <xref linkend="auth-peer">.
-          </para>
-         </listitem>
-        </varlistentry>
-
-        <varlistentry id="libpq-connect-krbsrvname" xreflabel="krbsrvname">
-         <term><literal>krbsrvname</literal></term>
-         <listitem>
-          <para>
-           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
-           <xref linkend="kerberos-auth"> and <xref linkend="gssapi-auth">.)
-          </para>
-         </listitem>
-        </varlistentry>
-
-        <varlistentry id="libpq-connect-gsslib" xreflabel="gsslib">
-         <term><literal>gsslib</literal></term>
-         <listitem>
-          <para>
-           GSS library to use for GSSAPI authentication. Only used on Windows.
-           Set to <literal>gssapi</literal> to force libpq to use the GSSAPI
-           library for authentication instead of the default SSPI.
-          </para>
-         </listitem>
-        </varlistentry>
-
-        <varlistentry id="libpq-connect-service" xreflabel="service">
-         <term><literal>service</literal></term>
-         <listitem>
-          <para>
-           Service name to use for additional parameters.  It specifies a service
-           name in <filename>pg_service.conf</filename> that holds additional connection parameters.
-           This allows applications to specify only a service name so connection parameters
-           can be centrally maintained. See <xref linkend="libpq-pgservice">.
-          </para>
-         </listitem>
-        </varlistentry>
-       </variablelist>
-
        If  any  parameter is unspecified, then the corresponding
        environment variable (see <xref linkend="libpq-envars">)
        is checked. If the  environment  variable is not set either,
@@ -638,20 +143,11 @@ PGconn *PQconnectdbParams(const char * const *keywords,
       </para>
 
       <para>
-        If <literal>expand_dbname</literal> is non-zero and
-        <parameter>dbname</parameter> contains an <symbol>=</symbol> sign, it
-        is taken as a <parameter>conninfo</parameter> string in exactly the same way as
-        if it had been passed to <function>PQconnectdb</function>(see below). Previously
-        processed key words will be overridden by key words in the
-        <parameter>conninfo</parameter> string.
-      </para>
-
-      <para>
-        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
-        <parameter>dbname</parameter> key word, it is possible to determine what may
-        be overridden by a <parameter>conninfo</parameter> 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
+       <parameter>dbname</parameter> key word, it is possible to determine what may
+       be overridden by a <parameter>conninfo</parameter> string, and what may not.
       </para>
 
      </listitem>
@@ -675,19 +171,13 @@ PGconn *PQconnectdb(const char *conninfo);
 
       <para>
        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 <literal>keyword = value</literal>.
-       Spaces around the equal sign are optional. To write an empty value,
-       or a value containing spaces, surround it with single quotes, e.g.,
-       <literal>keyword = 'a value'</literal>. Single quotes and backslashes
-       within the value must be escaped with a backslash, i.e.,
-       <literal>\'</literal> and <literal>\\</literal>.
-      </para>
+       contain one or more parameter settings separated by whitespace,
+       or it can contain a <acronym>URI</acronym>.
+       See <xref linkend="libpq-connstring"> for details.
+     </para>
 
-      <para>
-       The currently recognized parameter key words are the same as above.
-      </para>
-     </listitem>
+
+    </listitem>
     </varlistentry>
 
     <varlistentry id="libpq-pqsetdblogin">
@@ -714,10 +204,11 @@ PGconn *PQsetdbLogin(const char *pghost,
       </para>
 
       <para>
-        If the <parameter>dbName</parameter> contains an <symbol>=</symbol> sign, it
+        If the <parameter>dbName</parameter> contains
+        an <symbol>=</symbol> sign or has a valid connection <acronym>URI</acronym> prefix, it
         is taken as a <parameter>conninfo</parameter> string in exactly the same way as
         if it had been passed to <function>PQconnectdb</function>, and the remaining
-        parameters are then applied as above.
+        parameters are then applied as specified for <function>PQconnectdbParams</>.
       </para>
      </listitem>
     </varlistentry>
@@ -795,7 +286,7 @@ PostgresPollingStatusType PQconnectPoll(PGconn *conn);
          <para>
           The <literal>hostaddr</> and <literal>host</> parameters are used appropriately to ensure that
           name and reverse name queries are not made. See the documentation of
-          these parameters under <function>PQconnectdbParams</function> above for details.
+          these parameters in <xref linkend="libpq-paramkeywords"> for details.
          </para>
         </listitem>
 
@@ -1219,6 +710,617 @@ PGPing PQping(const char *conninfo);
 
    </variablelist>
   </para>
+
+  <sect2 id="libpq-paramkeywords">
+   <title>Parameter Key Words</title>
+
+   <para>
+    The currently recognized parameter key words are:
+
+    <variablelist>
+     <varlistentry id="libpq-connect-host" xreflabel="host">
+      <term><literal>host</literal></term>
+      <listitem>
+       <para>
+        Name of host to connect to.<indexterm><primary>host name</></>
+        If this begins with a slash, it specifies Unix-domain
+        communication rather than TCP/IP communication; the value is the
+        name of the directory in which the socket file is stored.  The
+        default behavior when <literal>host</literal> is not specified
+        is to connect to a Unix-domain
+        socket<indexterm><primary>Unix domain socket</></> in
+        <filename>/tmp</filename> (or whatever socket directory was specified
+        when <productname>PostgreSQL</> was built). On machines without
+        Unix-domain sockets, the default is to connect to <literal>localhost</>.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry id="libpq-connect-hostaddr" xreflabel="hostaddr">
+      <term><literal>hostaddr</literal></term>
+      <listitem>
+       <para>
+        Numeric IP address of host to connect to.  This should be in the
+        standard IPv4 address format, e.g., <literal>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.
+       </para>
+
+       <para>
+        Using <literal>hostaddr</> instead of <literal>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 <literal>verify-full</> SSL
+        certificate verification.  The following rules are used:
+        <itemizedlist>
+         <listitem>
+          <para>
+           If <literal>host</> is specified without <literal>hostaddr</>,
+           a host name lookup occurs.
+          </para>
+         </listitem>
+         <listitem>
+          <para>
+           If <literal>hostaddr</> is specified without <literal>host</>,
+           the value for <literal>hostaddr</> gives the server network address.
+           The connection attempt will fail if the authentication
+           method requires a host name.
+          </para>
+         </listitem>
+         <listitem>
+          <para>
+           If both <literal>host</> and <literal>hostaddr</> are specified,
+           the value for <literal>hostaddr</> gives the server network address.
+           The value for <literal>host</> is ignored unless the
+           authentication method requires it, in which case it will be
+           used as the host name.
+          </para>
+         </listitem>
+        </itemizedlist>
+        Note that authentication is likely to fail if <literal>host</>
+        is not the name of the server at network address <literal>hostaddr</>.
+        Also, note that <literal>host</> rather than <literal>hostaddr</>
+        is used to identify the connection in <filename>~/.pgpass</> (see
+        <xref linkend="libpq-pgpass">).
+       </para>
+
+       <para>
+        Without either a host name or host address,
+        <application>libpq</application> will connect using a
+        local Unix-domain socket; or on machines without Unix-domain
+        sockets, it will attempt to connect to <literal>localhost</>.
+       </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry id="libpq-connect-port" xreflabel="port">
+       <term><literal>port</literal></term>
+       <listitem>
+       <para>
+        Port number to connect to at the server host, or socket file
+        name extension for Unix-domain
+        connections.<indexterm><primary>port</></>
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry id="libpq-connect-dbname" xreflabel="dbname">
+      <term><literal>dbname</literal></term>
+      <listitem>
+      <para>
+       The database name.  Defaults to be the same as the user name.
+       In certain contexts, the value is checked for extended
+       formats; see <xref linkend="libpq-connstring"> for more details on
+       those.
+      </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry id="libpq-connect-user" xreflabel="user">
+      <term><literal>user</literal></term>
+      <listitem>
+      <para>
+       <productname>PostgreSQL</productname> user name to connect as.
+       Defaults to be the same as the operating system name of the user
+       running the application.
+      </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry id="libpq-connect-password" xreflabel="password">
+      <term><literal>password</literal></term>
+      <listitem>
+      <para>
+       Password to be used if the server demands password authentication.
+      </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry id="libpq-connect-connect-timeout" xreflabel="connect_timeout">
+      <term><literal>connect_timeout</literal></term>
+      <listitem>
+      <para>
+       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.
+      </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry id="libpq-connect-client-encoding" xreflabel="client_encoding">
+      <term><literal>client_encoding</literal></term>
+      <listitem>
+      <para>
+       This sets the <varname>client_encoding</varname>
+       configuration parameter for this connection.  In addition to
+       the values accepted by the corresponding server option, you
+       can use <literal>auto</literal> to determine the right
+       encoding from the current locale in the client
+       (<envar>LC_CTYPE</envar> environment variable on Unix
+       systems).
+      </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry id="libpq-connect-options" xreflabel="options">
+      <term><literal>options</literal></term>
+      <listitem>
+       <para>
+        Adds command-line options to send to the server at run-time.
+        For example, setting this to <literal>-c geqo=off</> sets the
+        session's value of the <varname>geqo</> parameter to
+        <literal>off</>.  For a detailed discussion of the available
+        options, consult <xref linkend="runtime-config">.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry id="libpq-connect-application-name" xreflabel="application_name">
+      <term><literal>application_name</literal></term>
+      <listitem>
+       <para>
+        Specifies a value for the <xref linkend="guc-application-name">
+        configuration parameter.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry id="libpq-connect-fallback-application-name" xreflabel="fallback_application_name">
+      <term><literal>fallback_application_name</literal></term>
+      <listitem>
+       <para>
+        Specifies a fallback value for the <xref
+        linkend="guc-application-name"> configuration parameter.
+        This value will be used if no value has been given for
+        <literal>application_name</> via a connection parameter or the
+        <envar>PGAPPNAME</envar> 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.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry id="libpq-keepalives" xreflabel="keepalives">
+      <term><literal>keepalives</literal></term>
+      <listitem>
+       <para>
+        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.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry id="libpq-keepalives-idle" xreflabel="keepalives_idle">
+      <term><literal>keepalives_idle</literal></term>
+      <listitem>
+       <para>
+        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 <symbol>TCP_KEEPIDLE</> or <symbol>TCP_KEEPALIVE</>
+        socket option is available, and on Windows; on other systems, it has no
+        effect.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry id="libpq-keepalives-interval" xreflabel="keepalives_interval">
+      <term><literal>keepalives_interval</literal></term>
+      <listitem>
+       <para>
+        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 <symbol>TCP_KEEPINTVL</>
+        socket option is available, and on Windows; on other systems, it has no
+        effect.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry id="libpq-keepalives-count" xreflabel="keepalives_count">
+      <term><literal>keepalives_count</literal></term>
+      <listitem>
+       <para>
+        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 <symbol>TCP_KEEPCNT</>
+        socket option is available; on other systems, it has no effect.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry id="libpq-connect-tty" xreflabel="tty">
+      <term><literal>tty</literal></term>
+      <listitem>
+      <para>
+       Ignored (formerly, this specified where to send server debug output).
+      </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry id="libpq-connect-sslmode" xreflabel="sslmode">
+      <term><literal>sslmode</literal></term>
+      <listitem>
+       <para>
+        This option determines whether or with what priority a secure
+        <acronym>SSL</> TCP/IP connection will be negotiated with the
+        server. There are six modes:
+
+        <variablelist>
+         <varlistentry>
+          <term><literal>disable</literal></term>
+          <listitem>
+           <para>
+            only try a non-<acronym>SSL</> connection
+           </para>
+          </listitem>
+         </varlistentry>
+
+         <varlistentry>
+          <term><literal>allow</literal></term>
+          <listitem>
+           <para>
+            first try a non-<acronym>SSL</> connection; if that
+            fails, try an <acronym>SSL</> connection
+           </para>
+          </listitem>
+         </varlistentry>
+
+         <varlistentry>
+          <term><literal>prefer</literal> (default)</term>
+          <listitem>
+           <para>
+            first try an <acronym>SSL</> connection; if that fails,
+            try a non-<acronym>SSL</> connection
+           </para>
+          </listitem>
+         </varlistentry>
+
+         <varlistentry>
+          <term><literal>require</literal></term>
+          <listitem>
+           <para>
+            only try an <acronym>SSL</> connection. If a root CA
+            file is present, verify the certificate in the same way as
+            if <literal>verify-ca</literal> was specified
+           </para>
+          </listitem>
+         </varlistentry>
+
+         <varlistentry>
+          <term><literal>verify-ca</literal></term>
+          <listitem>
+           <para>
+            only try an <acronym>SSL</> connection, and verify that
+            the server certificate is issued by a trusted
+            certificate authority (<acronym>CA</>)
+           </para>
+          </listitem>
+         </varlistentry>
+
+         <varlistentry>
+          <term><literal>verify-full</literal></term>
+          <listitem>
+           <para>
+            only try an <acronym>SSL</> connection, verify that the
+            server certificate is issued by a
+            trusted <acronym>CA</> and that the server host name
+            matches that in the certificate
+           </para>
+          </listitem>
+         </varlistentry>
+        </variablelist>
+
+        See <xref linkend="libpq-ssl"> for a detailed description of how
+        these options work.
+       </para>
+
+       <para>
+        <literal>sslmode</> is ignored for Unix domain socket
+        communication.
+        If <productname>PostgreSQL</> is compiled without SSL support,
+        using options <literal>require</>, <literal>verify-ca</>, or
+        <literal>verify-full</> will cause an error, while
+        options <literal>allow</> and <literal>prefer</> will be
+        accepted but <application>libpq</> will not actually attempt
+        an <acronym>SSL</>
+        connection.<indexterm><primary>SSL</><secondary
+        sortas="libpq">with libpq</></indexterm>
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry id="libpq-connect-requiressl" xreflabel="requiressl">
+      <term><literal>requiressl</literal></term>
+      <listitem>
+       <para>
+        This option is deprecated in favor of the <literal>sslmode</>
+        setting.
+       </para>
+
+       <para>
+        If set to 1, an <acronym>SSL</acronym> connection to the server
+        is required (this is equivalent to <literal>sslmode</>
+        <literal>require</>).  <application>libpq</> will then refuse
+        to connect if the server does not accept an
+        <acronym>SSL</acronym> connection.  If set to 0 (default),
+        <application>libpq</> will negotiate the connection type with
+        the server (equivalent to <literal>sslmode</>
+        <literal>prefer</>).  This option is only available if
+        <productname>PostgreSQL</> is compiled with SSL support.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry id="libpq-connect-sslcompression" xreflabel="sslcompression">
+      <term><literal>sslcompression</literal></term>
+      <listitem>
+       <para>
+        If set to 1 (default), data sent over SSL connections will be
+        compressed (this requires <productname>OpenSSL</> version
+        0.9.8 or later).
+        If set to 0, compression will be disabled (this requires
+        <productname>OpenSSL</> 1.0.0 or later).
+        This parameter is ignored if a connection without SSL is made,
+        or if the version of <productname>OpenSSL</> used does not support
+        it.
+       </para>
+       <para>
+        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.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry id="libpq-connect-sslcert" xreflabel="sslcert">
+      <term><literal>sslcert</literal></term>
+      <listitem>
+       <para>
+        This parameter specifies the file name of the client SSL
+        certificate, replacing the default
+        <filename>~/.postgresql/postgresql.crt</>.
+        This parameter is ignored if an SSL connection is not made.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry id="libpq-connect-sslkey" xreflabel="sslkey">
+      <term><literal>sslkey</literal></term>
+      <listitem>
+       <para>
+        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
+        <filename>~/.postgresql/postgresql.key</>, or it can specify a key
+        obtained from an external <quote>engine</> (engines are
+        <productname>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.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry id="libpq-connect-sslrootcert" xreflabel="sslrootcert">
+      <term><literal>sslrootcert</literal></term>
+      <listitem>
+       <para>
+        This parameter specifies the name of a file containing SSL
+        certificate authority (<acronym>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
+        <filename>~/.postgresql/root.crt</>.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry id="libpq-connect-sslcrl" xreflabel="sslcrl">
+      <term><literal>sslcrl</literal></term>
+      <listitem>
+       <para>
+        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
+        <filename>~/.postgresql/root.crl</>.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry id="libpq-connect-requirepeer" xreflabel="requirepeer">
+      <term><literal>requirepeer</literal></term>
+      <listitem>
+       <para>
+        This parameter specifies the operating-system user name of the
+        server, for example <literal>requirepeer=postgres</literal>.
+        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
+        <filename>/tmp</filename> 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
+        <literal>peer</> authentication method is implemented; see
+        <xref linkend="auth-peer">.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry id="libpq-connect-krbsrvname" xreflabel="krbsrvname">
+      <term><literal>krbsrvname</literal></term>
+      <listitem>
+       <para>
+        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
+        <xref linkend="kerberos-auth"> and <xref linkend="gssapi-auth">.)
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry id="libpq-connect-gsslib" xreflabel="gsslib">
+      <term><literal>gsslib</literal></term>
+      <listitem>
+       <para>
+        GSS library to use for GSSAPI authentication. Only used on Windows.
+        Set to <literal>gssapi</literal> to force libpq to use the GSSAPI
+        library for authentication instead of the default SSPI.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry id="libpq-connect-service" xreflabel="service">
+      <term><literal>service</literal></term>
+      <listitem>
+       <para>
+        Service name to use for additional parameters.  It specifies a service
+        name in <filename>pg_service.conf</filename> that holds additional connection parameters.
+        This allows applications to specify only a service name so connection parameters
+        can be centrally maintained. See <xref linkend="libpq-pgservice">.
+       </para>
+      </listitem>
+     </varlistentry>
+    </variablelist>
+   </para>
+  </sect2>
+
+  <sect2 id="libpq-connstring">
+   <title>Connection Strings</title>
+
+   <indexterm zone="libpq-connstring">
+    <primary><literal>conninfo</literal></primary>
+   </indexterm>
+
+   <indexterm zone="libpq-connstring">
+    <primary><literal>URI</literal></primary>
+   </indexterm>
+
+   <para>
+    Several <application>libpq</> functions parse a user-specified string to obtain
+    connection parameters.  There are two accepted formats for these strings:
+    plain <literal>keyword = value</literal> strings, and URIs.
+   </para>
+
+   <para>
+    In the first format, each parameter setting is in the form
+    <literal>keyword = value</literal>.  Spaces around the equal sign are
+    optional. To write an empty value, or a value containing spaces, surround it
+    with single quotes, e.g., <literal>keyword = 'a value'</literal>. Single
+    quotes and backslashes within
+    the value must be escaped with a backslash, i.e., <literal>\'</literal> and
+    <literal>\\</literal>.
+   </para>
+
+   <para>
+    The currently recognized parameter key words are listed in
+    <xref linkend="libpq-paramkeywords">.
+   </para>
+
+   <para>
+   The general form for connection <acronym>URI</acronym> is the
+   following:
+<synopsis>
+postgresql://[user[:password]@][unix-socket][:port[/dbname]][?param1=value1&amp;...]
+postgresql://[user[:password]@][net-location][:port][/dbname][?param1=value1&amp;...]
+</synopsis>
+   </para>
+
+   <para>
+    The <acronym>URI</acronym> designator can be either
+    <literal>postgresql://</literal> or <literal>postgres://</literal> and
+    each of the <acronym>URI</acronym> parts is optional.  The following
+    examples illustrate valid <acronym>URI</acronym> syntax uses:
+<synopsis>
+postgresql://
+postgresql://localhost
+postgresql://localhost:5433
+postgresql://localhost/mydb
+postgresql://user@localhost
+postgresql://user:secret@localhost
+postgresql://other@localhost/otherdb
+</synopsis>
+   </para>
+
+   <para>
+    Percent-encoding may be used to include a symbol with special meaning in
+    any of the <acronym>URI</acronym> parts.
+   </para>
+
+   <para>
+    Additional connection parameters may optionally follow the base <acronym>URI</acronym>.
+    Any connection parameters not corresponding to key words listed
+    in <xref linkend="libpq-paramkeywords"> are ignored and a warning message
+    about them is sent to <filename>stderr</filename>.
+   </para>
+
+   <para>
+    For improved compatibility with JDBC connection <acronym>URI</acronym>
+    syntax, instances of parameter <literal>ssl=true</literal> are translated
+    into <literal>sslmode=require</literal> (see above.)
+   </para>
+
+   <para>
+    The host part may be either hostname or an IP address.  To specify an
+    IPv6 host address, enclose it in square brackets:
+<synopsis>
+postgresql://[2001:db8::1234]/database
+</synopsis>
+    As a special case, a host part which starts with <symbol>/</symbol> is
+    treated as a local Unix socket directory to look for the connection
+    socket special file:
+<synopsis>
+postgresql:///path/to/pgsql/socket/dir
+</synopsis>
+    The whole connection string up to the extra parameters designator
+    (<symbol>?</symbol>) or the port designator (<symbol>:</symbol>) is treated
+    as the absolute path to the socket directory
+    (<literal>/path/to/pgsql/socket/dir</literal> in this example.)  To specify
+    a non-default database name in this case you can use either of the following
+    syntaxes:
+<synopsis>
+postgresql:///path/to/pgsql/socket/dir?dbname=otherdb
+postgresql:///path/to/pgsql/socket/dir:5432/otherdb
+</synopsis>
+   </para>
+  </sect2>
+
  </sect1>
 
  <sect1 id="libpq-status">
diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index b8491015f4eb205d095fe4bcc5aaaef6a3cb1b39..bdcadf3692864dd665bec195067f2c5d55c8c695 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.
       </para>
       <para>
-       If this parameter contains an <symbol>=</symbol> sign, it is treated as a
+       If this parameter contains an <symbol>=</symbol> sign or starts
+       with a valid <acronym>URI</acronym> prefix
+       (<literal>postgresql://</literal>
+       or <literal>postgres://</literal>), it is treated as a
        <parameter>conninfo</parameter> string. See <xref linkend="libpq-connect"> for more information.
       </para>
       </listitem>
@@ -596,11 +599,13 @@ PostgreSQL documentation
 
     <para>
      An alternative way to specify connection parameters is in a
-     <parameter>conninfo</parameter> string, which is used instead of a
-     database name. This mechanism give you very wide control over the
+     <parameter>conninfo</parameter> string or
+     a <acronym>URI</acronym>, which is used instead of a database
+     name. This mechanism give you very wide control over the
      connection. For example:
 <programlisting>
 $ <userinput>psql "service=myservice sslmode=require"</userinput>
+$ <userinput>psql postgresql://dbmaster:5433/mydb?sslmode=require</userinput>
 </programlisting>
      This way you can also use LDAP for connection parameter lookup as
      described in <xref linkend="libpq-ldap">.
diff --git a/src/interfaces/libpq/Makefile b/src/interfaces/libpq/Makefile
index d1ded1f0e59f401f1e4509951ebb020ad5a4f60a..ec4fdd403e4f390d0cdfe1f876c88eca4732d3e3 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 03fd6e45bb9a9b996f39b6c2625643ac8a288ebe..d0b2ea47cbeea6c80a0ff3251ad47d030dc3ec51 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.
-		 */
-		for (option = options; option->keyword != NULL; option++)
-		{
-			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
+		 * Now that we have the name and the value, store the record.
 		 */
-		if (option->val)
-			free(option->val);
-		option->val = strdup(pval);
-		if (!option->val)
+		if (!conninfo_storeval(options, pname, pval, errorMessage, false, false))
 		{
-			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 0000000000000000000000000000000000000000..b9023c37f32d62d6a1ecf36bf170576c1f2b5583
--- /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 0000000000000000000000000000000000000000..001ecc378dac3c8c5b1f58a6209690a0f5173f67
--- /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 0000000000000000000000000000000000000000..54a6291bc4389649d8d327f9faece23eb1fea14b
--- /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 0000000000000000000000000000000000000000..8d33ae1ac1293560b07fcacbed9bb91a9f0824eb
--- /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 0000000000000000000000000000000000000000..298d8bdc4a2a1002b37eb7d75f41ae7aa6ed5b35
--- /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 0000000000000000000000000000000000000000..17fcce9fb27a66332d1f4352327c80b18dee5529
--- /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;
+}