libpq-uri-rfc-compliance.patch

application/octet-stream

Filename: libpq-uri-rfc-compliance.patch
Type: application/octet-stream
Part: 0
Message: Re: libpq URL syntax vs SQLAlchemy

Patch

Same data as JSON: GET /api/v1/attachments/:id/patch the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes. API reference →
Format: context
File+
doc/src/sgml/libpq.sgml 19 0
src/interfaces/libpq/fe-connect.c 104 0
src/interfaces/libpq/test/expected.out 32 0
src/interfaces/libpq/test/regress.in 11 0
*** a/doc/src/sgml/libpq.sgml
--- b/doc/src/sgml/libpq.sgml
***************
*** 1235,1241 **** PGPing PQping(const char *conninfo);
     <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>
--- 1235,1243 ----
     <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 <ulink url="http://www.ietf.org/rfc/rfc3986.txt">RFC
!     3986</ulink> URIs.
     </para>
  
     <para>
***************
*** 1257,1264 **** PGPing PQping(const char *conninfo);
     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>
  
--- 1259,1265 ----
     The general form for connection <acronym>URI</acronym> is the
     following:
  <synopsis>
! postgresql://[user[:password]@][netloc][:port][/dbname][?param1=value1&amp;...]
  </synopsis>
     </para>
  
***************
*** 1302,1323 **** postgresql://other@localhost/otherdb
  <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>
  
--- 1303,1323 ----
  <synopsis>
  postgresql://[2001:db8::1234]/database
  </synopsis>
!    </para>
!    <para>
!     If the host part is present, a TCP/IP connection is initiated.  If
!     the host part is empty, the connection is made by the means of
!     local Unix sockets (on platforms which support that.)  To specify
!     a non-standard local Unix socket directory to look for the
!     connection socket special file, omit the host specification in the
!     URI and use the optional host parameter (the parameter value
!     should start with a slash <literal>/</>, and is treated as the
!     absolute path:)
! <synopsis>
! postgresql:///dbname?host=/path/to/pgsql/socket/dir
! </synopsis>
!     The database name in above syntax may be also omitted, in this
!     case the connection is made to the default database.
     </para>
    </sect2>
  
*** a/src/interfaces/libpq/fe-connect.c
--- b/src/interfaces/libpq/fe-connect.c
***************
*** 4544,4561 **** conninfo_uri_parse(const char *uri, PQExpBuffer errorMessage,
   * 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,
--- 4544,4558 ----
   * 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 (RFC
!  * 3986):
   *
!  * postgresql://[user[:password]@][netloc][:port][/dbname][?param1=value1&...]
   *
!  * where "netloc" is a hostname, an IPv4 address, or an IPv6 address surrounded
!  * by literal square brackets.
   *
!  * Any of the URI parts might use percent-encoding (%xy).
   */
  static bool
  conninfo_uri_parse_options(PQconninfoOption *options, const char *uri,
***************
*** 4566,4571 **** conninfo_uri_parse_options(PQconninfoOption *options, const char *uri,
--- 4563,4570 ----
  	char   *buf = strdup(uri);	/* need a modifiable copy of the input URI */
  	char   *start = buf;
  	char	prevchar = '\0';
+ 	char   *user = NULL;
+ 	char   *host = NULL;
  	bool	retval = false;
  
  	if (buf == NULL)
***************
*** 4593,4600 **** conninfo_uri_parse_options(PQconninfoOption *options, const char *uri,
  		++p;
  	if (*p == '@')
  	{
- 		char   *user;
- 
  		/*
  		 * Found username/password designator, so URI should be of the form
  		 * "scheme://user[:password]@[netloc]".
--- 4592,4597 ----
***************
*** 4609,4622 **** conninfo_uri_parse_options(PQconninfoOption *options, const char *uri,
  		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;
  
--- 4606,4613 ----
  		prevchar = *p;
  		*p = '\0';
  
! 		if (*user &&
! 			!conninfo_storeval(options, "user", user,
  							   errorMessage, false, true))
  			goto cleanup;
  
***************
*** 4628,4642 **** conninfo_uri_parse_options(PQconninfoOption *options, const char *uri,
  				++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;
  		}
--- 4619,4626 ----
  				++p;
  			*p = '\0';
  
! 			if (*password &&
! 				!conninfo_storeval(options, "password", password,
  								   errorMessage, false, true))
  				goto cleanup;
  		}
***************
*** 4656,4742 **** conninfo_uri_parse_options(PQconninfoOption *options, const char *uri,
  	 * "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 == ':')
  	{
--- 4640,4704 ----
  	 * "p" has been incremented past optional URI credential information at
  	 * this point and now points at the "netloc" part of the URI.
  	 *
! 	 * Look for IPv6 address.
  	 */
! 	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 (*host &&
! 		!conninfo_storeval(options, "host", host,
! 						   errorMessage, false, true))
! 		goto cleanup;
  
  
  	if (prevchar == ':')
  	{
***************
*** 4748,4761 **** conninfo_uri_parse_options(PQconninfoOption *options, const char *uri,
  		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;
  	}
--- 4710,4717 ----
  		prevchar = *p;
  		*p = '\0';
  
! 		if (*port &&
! 			!conninfo_storeval(options, "port", port,
  							   errorMessage, false, true))
  			goto cleanup;
  	}
***************
*** 4813,4821 **** conninfo_uri_parse_params(char *params,
  {
  	while (*params)
  	{
! 		const char *keyword = params;
! 		const char *value = NULL;
  		char *p = params;
  
  		/*
  		 * Scan the params string for '=' and '&', marking the end of keyword
--- 4769,4778 ----
  {
  	while (*params)
  	{
! 		char *keyword = params;
! 		char *value = NULL;
  		char *p = params;
+ 		bool malloced = false;
  
  		/*
  		 * Scan the params string for '=' and '&', marking the end of keyword
***************
*** 4866,4900 **** conninfo_uri_parse_params(char *params,
  			++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;
--- 4823,4888 ----
  			++p;
  		}
  
+ 		keyword = conninfo_uri_decode(keyword, errorMessage);
+ 		if (keyword == NULL)
+ 		{
+ 			/* conninfo_uri_decode already set an error message */
+ 			return false;
+ 		}
+ 		value = conninfo_uri_decode(value, errorMessage);
+ 		if (value == NULL)
+ 		{
+ 			/* conninfo_uri_decode already set an error message */
+ 			free(keyword);
+ 			return false;
+ 		}
+ 		malloced = true;
+ 
  		/*
! 		 * Special keyword handling for improved JDBC compatibility.
  		 */
  		if (strcmp(keyword, "ssl") == 0 &&
  			strcmp(value, "true") == 0)
  		{
+ 			free(keyword);
+ 			free(value);
+ 			malloced = false;
+ 
  			keyword = "sslmode";
  			value = "require";
  		}
  
  		/*
  		 * Store the value if the corresponding option exists; ignore
! 		 * otherwise.  At this point both keyword and value are not
! 		 * URI-encoded.
  		 */
  		if (!conninfo_storeval(connOptions, keyword, value,
! 							   errorMessage, true, false))
  		{
  			/*
  			 * Check if there was a hard error when decoding or storing the
  			 * option.
  			 */
  			if (errorMessage->len != 0)
+ 			{
+ 				if (malloced)
+ 				{
+ 					free(keyword);
+ 					free(value);
+ 				}
  				return false;
+ 			}
  
  			fprintf(stderr,
  					libpq_gettext("WARNING: ignoring unrecognized URI query parameter: %s\n"),
  					keyword);
  		}
+ 		if (malloced)
+ 		{
+ 			free(keyword);
+ 			free(value);
+ 		}
  
  		/* Proceed to next key=value pair */
  		params = p;
***************
*** 5017,5023 **** conninfo_getval(PQconninfoOption *connOptions,
   * 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.
--- 5005,5012 ----
   * Store a (new) value for an option corresponding to the keyword in
   * connOptions array.
   *
!  * If uri_decode is true, the value is URI-decoded.  The keyword is always
!  * assumed to be non URI-encoded.
   *
   * If successful, returns a pointer to the corresponding PQconninfoOption,
   * which value is replaced with a strdup'd copy of the passed value string.
***************
*** 5034,5065 **** conninfo_storeval(PQconninfoOption *connOptions,
  				  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)
--- 5023,5038 ----
  				  bool uri_decode)
  {
  	PQconninfoOption *option;
! 	char			 *value_copy;
  
! 	option = conninfo_find(connOptions, keyword);
  	if (option == NULL)
  	{
  		if (!ignoreMissing)
  			printfPQExpBuffer(errorMessage,
  							  libpq_gettext("invalid connection option \"%s\"\n"),
  							  keyword);
! 		return NULL;
  	}
  
  	if (uri_decode)
***************
*** 5067,5073 **** conninfo_storeval(PQconninfoOption *connOptions,
  		value_copy = conninfo_uri_decode(value, errorMessage);
  		if (value_copy == NULL)
  			/* conninfo_uri_decode already set an error message */
! 			goto failed;
  	}
  	else
  	{
--- 5040,5046 ----
  		value_copy = conninfo_uri_decode(value, errorMessage);
  		if (value_copy == NULL)
  			/* conninfo_uri_decode already set an error message */
! 			return NULL;
  	}
  	else
  	{
***************
*** 5076,5082 **** conninfo_storeval(PQconninfoOption *connOptions,
  		if (value_copy == NULL)
  		{
  			printfPQExpBuffer(errorMessage, libpq_gettext("out of memory\n"));
! 			goto failed;
  		}
  	}
  
--- 5049,5055 ----
  		if (value_copy == NULL)
  		{
  			printfPQExpBuffer(errorMessage, libpq_gettext("out of memory\n"));
! 			return NULL;
  		}
  	}
  
***************
*** 5084,5097 **** conninfo_storeval(PQconninfoOption *connOptions,
  		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;
  }
  
  /*
--- 5057,5063 ----
*** a/src/interfaces/libpq/test/expected.out
--- b/src/interfaces/libpq/test/expected.out
***************
*** 20,26 **** 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)
--- 20,26 ----
  user='uri-user' host='host' (inet)
  
  trying postgresql://uri-user@
! user='uri-user' (local)
  
  trying postgresql://host:12345/
  host='host' port='12345' (inet)
***************
*** 38,47 **** 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)
--- 38,47 ----
  host='host' (inet)
  
  trying postgresql://
! (local)
  
  trying postgresql://?hostaddr=127.0.0.1
! hostaddr='127.0.0.1' (inet)
  
  trying postgresql://example.com?hostaddr=63.1.2.4
  host='example.com' hostaddr='63.1.2.4' (inet)
***************
*** 59,65 **** 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
--- 59,65 ----
  user='someotheruser' dbname='db' host='host' port='12345' (inet)
  
  trying postgresql://host/db?u%7aer=someotheruser&port=12345
! WARNING: ignoring unrecognized URI query parameter: uzer
  dbname='db' host='host' port='12345' (inet)
  
  trying postgresql://host:12345?user=uri-user
***************
*** 87,96 **** 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
--- 87,105 ----
  host='::1' (inet)
  
  trying postgres://
! (local)
! 
! trying postgres:///
! (local)
! 
! trying postgres:///db
! dbname='db' (local)
  
! trying postgres://uri-user@/db
! user='uri-user' dbname='db' (local)
! 
! trying postgres://?host=/path/to/socket/dir
! host='/path/to/socket/dir' (local)
  
  trying postgresql://host?uzer=
  WARNING: ignoring unrecognized URI query parameter: uzer
***************
*** 145,163 **** 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)
  
--- 154,182 ----
  
  
  trying postgres://@host
! host='host' (inet)
  
  trying postgres://host:/
! host='host' (inet)
! 
! trying postgres://:12345/
! port='12345' (local)
  
+ trying postgres://otheruser@?host=/no/such/directory
+ user='otheruser' host='/no/such/directory' (local)
  
! trying postgres://otheruser@/?host=/no/such/directory
  user='otheruser' host='/no/such/directory' (local)
  
! trying postgres://otheruser@:12345?host=/no/such/socket/path
  user='otheruser' host='/no/such/socket/path' port='12345' (local)
  
! trying postgres://otheruser@:12345/db?host=/path/to/socket
  user='otheruser' dbname='db' host='/path/to/socket' port='12345' (local)
  
+ trying postgres://:12345/db?host=/path/to/socket
+ dbname='db' host='/path/to/socket' port='12345' (local)
+ 
+ trying postgres://:12345?host=/path/to/socket
+ host='/path/to/socket' port='12345' (local)
+ 
*** a/src/interfaces/libpq/test/regress.in
--- b/src/interfaces/libpq/test/regress.in
***************
*** 28,34 **** postgresql://[2001:db8::1234]/
  postgresql://[200z:db8::1234]/
  postgresql://[::1]
  postgres://
! postgres:///tmp
  postgresql://host?uzer=
  postgre://
  postgres://[::1
--- 28,37 ----
  postgresql://[200z:db8::1234]/
  postgresql://[::1]
  postgres://
! postgres:///
! postgres:///db
! postgres://uri-user@/db
! postgres://?host=/path/to/socket/dir
  postgresql://host?uzer=
  postgre://
  postgres://[::1
***************
*** 44,49 **** 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
--- 47,56 ----
  postgresql://%
  postgres://@host
  postgres://host:/
! postgres://:12345/
! postgres://otheruser@?host=/no/such/directory
! postgres://otheruser@/?host=/no/such/directory
! postgres://otheruser@:12345?host=/no/such/socket/path
! postgres://otheruser@:12345/db?host=/path/to/socket
! postgres://:12345/db?host=/path/to/socket
! postgres://:12345?host=/path/to/socket