fix psql \conninfo & \connect when using hostaddr

Started by Fabien COELHOabout 7 years ago40 messages
#1Fabien COELHO
coelho@cri.ensmp.fr
1 attachment(s)

Hello,

This is a follow-up to another patch I posted about libpq confusing
documentation & psql resulting behavior under host/hostaddr settings.

Although the first mostly documentation patch did not gather much
enthousiasm, I still think both issues deserve a fix.

About updating psql's behavior, without this patch:

sh> psql "host=foo hostaddr=127.0.0.1"

psql> \conninfo
You are connected to database "fabien" as user "fabien" on host "foo" at port "5432".
# NOPE, I'm really connected to localhost, foo does not even exist
# Other apparent inconsistencies are possible when hostaddr overrides
# "host" which is an socket directory or an IP.

psql> \c template1
could not translate host name "foo" to address: Name or service not known
Previous connection kept
# hmmm.... what is the meaning of reusing a connection?
# this issue was pointed out by Arthur Zakirov

After the patch:

sh> psql "host=foo hostaddr=127.0.0.1"

psql> \conninfo
You are connected to database "fabien" as user "fabien" on host "foo" (address "127.0.0.1") at port "5432".
# better

psql> \c template1
You are now connected to database "template1" as user "fabien".
# thanks

The patch adds a PQhostaddr() function to libpq which reports the
"hostaddr" setting or the current server ip. The function is used by psql
for \conninfo and when reusing parameters for \connect.

The messages are slightly more verbose because the IP is output. I think
that user asking for conninfo should survive to the more precise data.
This also comes handy if a host name resolves to several IPs (eg IPv6 and
IPv4, or several IPs...).

--
Fabien.

Attachments:

libpq-conn-1.patchtext/x-diff; charset=us-ascii; name=libpq-conn-1.patchDownload
diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index 82a440531b..310792e58d 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -1735,6 +1735,35 @@ char *PQhost(const PGconn *conn);
      </listitem>
     </varlistentry>
 
+
+    <varlistentry id="libpq-pqhostaddr">
+     <term>
+      <function>PQhostaddr</function>
+      <indexterm>
+       <primary>PQhostaddr</primary>
+      </indexterm>
+     </term>
+
+     <listitem>
+      <para>
+       Returns the actual server IP address of the active connection.
+       This can be the address a host name resolved to, or an IP address
+       provided through the <literal>hostaddr</literal> parameter.
+<synopsis>
+char *PQhostaddr(const PGconn *conn);
+</synopsis>
+      </para>
+
+      <para>
+       <function>PQhostaddr</function> returns <symbol>NULL</symbol> if the
+       <parameter>conn</parameter> argument is <symbol>NULL</symbol>.
+       Otherwise, if there is an error producing the host information (perhaps
+       if the connection has not been fully established or there was an
+       error), it returns an empty string.
+      </para>
+     </listitem>
+    </varlistentry>
+
     <varlistentry id="libpq-pqport">
      <term>
       <function>PQport</function>
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 5b4d54a442..d6f3a981b3 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -596,14 +596,23 @@ exec_command_conninfo(PsqlScanState scan_state, bool active_branch)
 		else
 		{
 			char	   *host = PQhost(pset.db);
+			char	   *hostaddr = PQhostaddr(pset.db);
 
-			/* If the host is an absolute path, the connection is via socket */
+			/* If the host is an absolute path, the connection is via socket unless overriden by hostaddr */
 			if (is_absolute_path(host))
-				printf(_("You are connected to database \"%s\" as user \"%s\" via socket in \"%s\" at port \"%s\".\n"),
-					   db, PQuser(pset.db), host, PQport(pset.db));
+				if (hostaddr && *hostaddr)
+					printf(_("You are connected to database \"%s\" as user \"%s\" on address \"%s\" at port \"%s\".\n"),
+						   db, PQuser(pset.db), hostaddr, PQport(pset.db));
+				else
+					printf(_("You are connected to database \"%s\" as user \"%s\" via socket in \"%s\" at port \"%s\".\n"),
+						   db, PQuser(pset.db), host, PQport(pset.db));
 			else
-				printf(_("You are connected to database \"%s\" as user \"%s\" on host \"%s\" at port \"%s\".\n"),
-					   db, PQuser(pset.db), host, PQport(pset.db));
+				if (hostaddr && *hostaddr && strcmp(host, hostaddr) != 0)
+					printf(_("You are connected to database \"%s\" as user \"%s\" on host \"%s\" (address \"%s\") at port \"%s\".\n"),
+						   db, PQuser(pset.db), host, hostaddr, PQport(pset.db));
+				else
+					printf(_("You are connected to database \"%s\" as user \"%s\" on host \"%s\" at port \"%s\".\n"),
+						   db, PQuser(pset.db), host, PQport(pset.db));
 			printSSLInfo();
 		}
 	}
@@ -2854,6 +2863,7 @@ do_connect(enum trivalue reuse_previous_specification,
 	PGconn	   *o_conn = pset.db,
 			   *n_conn;
 	char	   *password = NULL;
+	char	   *hostaddr = NULL;
 	bool		keep_password;
 	bool		has_connection_string;
 	bool		reuse_previous;
@@ -2894,12 +2904,22 @@ do_connect(enum trivalue reuse_previous_specification,
 	}
 
 	/* grab missing values from the old connection */
-	if (!user && reuse_previous)
-		user = PQuser(o_conn);
-	if (!host && reuse_previous)
-		host = PQhost(o_conn);
-	if (!port && reuse_previous)
-		port = PQport(o_conn);
+	if (reuse_previous)
+	{
+		if (!user)
+			user = PQuser(o_conn);
+		if (host && strcmp(host, PQhost(o_conn)) == 0)
+			/* if we are targetting the same host, reuse its hostaddr for consistency */
+			hostaddr = PQhostaddr(o_conn);
+		if (!host)
+		{
+			host = PQhost(o_conn);
+			/* also set hostaddr for consistency */
+			hostaddr = PQhostaddr(o_conn);
+		}
+		if (!port)
+			port = PQport(o_conn);
+	}
 
 	/*
 	 * Any change in the parameters read above makes us discard the password.
@@ -2961,13 +2981,18 @@ do_connect(enum trivalue reuse_previous_specification,
 
 	while (true)
 	{
-#define PARAMS_ARRAY_SIZE	8
+#define PARAMS_ARRAY_SIZE	9
 		const char **keywords = pg_malloc(PARAMS_ARRAY_SIZE * sizeof(*keywords));
 		const char **values = pg_malloc(PARAMS_ARRAY_SIZE * sizeof(*values));
 		int			paramnum = -1;
 
 		keywords[++paramnum] = "host";
 		values[paramnum] = host;
+		if (hostaddr && *hostaddr)
+		{
+			keywords[++paramnum] = "hostaddr";
+			values[paramnum] = hostaddr;
+		}
 		keywords[++paramnum] = "port";
 		values[paramnum] = port;
 		keywords[++paramnum] = "user";
@@ -3071,14 +3096,23 @@ do_connect(enum trivalue reuse_previous_specification,
 			param_is_newly_set(PQport(o_conn), PQport(pset.db)))
 		{
 			char	   *host = PQhost(pset.db);
+			char	   *hostaddr = PQhostaddr(pset.db);
 
 			/* If the host is an absolute path, the connection is via socket */
 			if (is_absolute_path(host))
-				printf(_("You are now connected to database \"%s\" as user \"%s\" via socket in \"%s\" at port \"%s\".\n"),
-					   PQdb(pset.db), PQuser(pset.db), host, PQport(pset.db));
+				if (hostaddr && *hostaddr)
+					printf(_("You are now connected to database \"%s\" as user \"%s\" on address \"%s\" at port \"%s\".\n"),
+						   PQdb(pset.db), PQuser(pset.db), hostaddr, PQport(pset.db));
+				else
+					printf(_("You are now connected to database \"%s\" as user \"%s\" via socket in \"%s\" at port \"%s\".\n"),
+						   PQdb(pset.db), PQuser(pset.db), host, PQport(pset.db));
 			else
-				printf(_("You are now connected to database \"%s\" as user \"%s\" on host \"%s\" at port \"%s\".\n"),
-					   PQdb(pset.db), PQuser(pset.db), host, PQport(pset.db));
+				if (hostaddr && *hostaddr && strcmp(host, hostaddr) != 0)
+					printf(_("You are now connected to database \"%s\" as user \"%s\" on host \"%s\" (address \"%s\") at port \"%s\".\n"),
+						   PQdb(pset.db), PQuser(pset.db), host, hostaddr, PQport(pset.db));
+				else
+					printf(_("You are now connected to database \"%s\" as user \"%s\" on host \"%s\" at port \"%s\".\n"),
+						   PQdb(pset.db), PQuser(pset.db), host, PQport(pset.db));
 		}
 		else
 			printf(_("You are now connected to database \"%s\" as user \"%s\".\n"),
diff --git a/src/interfaces/libpq/exports.txt b/src/interfaces/libpq/exports.txt
index 4359fae30d..cc9ee9ce6b 100644
--- a/src/interfaces/libpq/exports.txt
+++ b/src/interfaces/libpq/exports.txt
@@ -173,3 +173,4 @@ PQsetErrorContextVisibility 170
 PQresultVerboseErrorMessage 171
 PQencryptPasswordConn     172
 PQresultMemorySize        173
+PQhostaddr                174
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index d001bc513d..8e3acaf05d 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -1471,6 +1471,34 @@ connectNoDelay(PGconn *conn)
 	return 1;
 }
 
+static void
+getHostaddr(PGconn *conn, char host_addr[NI_MAXHOST])
+{
+	struct sockaddr_storage *addr = &conn->raddr.addr;
+
+	if (conn->connhost[conn->whichhost].type == CHT_HOST_ADDRESS)
+		strlcpy(host_addr, conn->connhost[conn->whichhost].hostaddr, NI_MAXHOST);
+	else if (addr->ss_family == AF_INET)
+	{
+		if (inet_net_ntop(AF_INET,
+						  &((struct sockaddr_in *) addr)->sin_addr.s_addr,
+						  32,
+						  host_addr, NI_MAXHOST) == NULL)
+			strcpy(host_addr, "???");
+	}
+#ifdef HAVE_IPV6
+	else if (addr->ss_family == AF_INET6)
+	{
+		if (inet_net_ntop(AF_INET6,
+						  &((struct sockaddr_in6 *) addr)->sin6_addr.s6_addr,
+						  128,
+						  host_addr, NI_MAXHOST) == NULL)
+			strcpy(host_addr, "???");
+	}
+#endif
+	else
+		strcpy(host_addr, "???");
+}
 
 /* ----------
  * connectFailureMessage -
@@ -1504,34 +1532,12 @@ connectFailureMessage(PGconn *conn, int errorno)
 		char		host_addr[NI_MAXHOST];
 		const char *displayed_host;
 		const char *displayed_port;
-		struct sockaddr_storage *addr = &conn->raddr.addr;
 
 		/*
 		 * Optionally display the network address with the hostname. This is
 		 * useful to distinguish between IPv4 and IPv6 connections.
 		 */
-		if (conn->connhost[conn->whichhost].type == CHT_HOST_ADDRESS)
-			strlcpy(host_addr, conn->connhost[conn->whichhost].hostaddr, NI_MAXHOST);
-		else if (addr->ss_family == AF_INET)
-		{
-			if (inet_net_ntop(AF_INET,
-							  &((struct sockaddr_in *) addr)->sin_addr.s_addr,
-							  32,
-							  host_addr, sizeof(host_addr)) == NULL)
-				strcpy(host_addr, "???");
-		}
-#ifdef HAVE_IPV6
-		else if (addr->ss_family == AF_INET6)
-		{
-			if (inet_net_ntop(AF_INET6,
-							  &((struct sockaddr_in6 *) addr)->sin6_addr.s6_addr,
-							  128,
-							  host_addr, sizeof(host_addr)) == NULL)
-				strcpy(host_addr, "???");
-		}
-#endif
-		else
-			strcpy(host_addr, "???");
+		getHostaddr(conn, host_addr);
 
 		/* To which host and port were we actually connecting? */
 		if (conn->connhost[conn->whichhost].type == CHT_HOST_ADDRESS)
@@ -2302,6 +2308,21 @@ keep_going:						/* We will come back to here until there is
 						   addr_cur->ai_addrlen);
 					conn->raddr.salen = addr_cur->ai_addrlen;
 
+
+					/* set connip */
+					if (conn->connip != NULL)
+					{
+						free(conn->connip);
+						conn->connip = NULL;
+					}
+
+					{
+						char		host_addr[NI_MAXHOST];
+						getHostaddr(conn, host_addr);
+						if (strcmp(host_addr, "???") != 0)
+							conn->connip = strdup(host_addr);
+					}
+
 					conn->sock = socket(addr_cur->ai_family, SOCK_STREAM, 0);
 					if (conn->sock == PGINVALID_SOCKET)
 					{
@@ -6172,6 +6193,25 @@ PQhost(const PGconn *conn)
 	return "";
 }
 
+char *
+PQhostaddr(const PGconn *conn)
+{
+	if (!conn)
+		return NULL;
+
+	if (conn->connhost != NULL)
+	{
+		if (conn->connhost[conn->whichhost].hostaddr != NULL &&
+			conn->connhost[conn->whichhost].hostaddr[0] != '\0')
+			return conn->connhost[conn->whichhost].hostaddr;
+
+		if (conn->connip != NULL)
+			return conn->connip;
+	}
+
+	return "";
+}
+
 char *
 PQport(const PGconn *conn)
 {
diff --git a/src/interfaces/libpq/libpq-fe.h b/src/interfaces/libpq/libpq-fe.h
index 52bd5d2cd8..3f13ddf092 100644
--- a/src/interfaces/libpq/libpq-fe.h
+++ b/src/interfaces/libpq/libpq-fe.h
@@ -312,6 +312,7 @@ extern char *PQdb(const PGconn *conn);
 extern char *PQuser(const PGconn *conn);
 extern char *PQpass(const PGconn *conn);
 extern char *PQhost(const PGconn *conn);
+extern char *PQhostaddr(const PGconn *conn);
 extern char *PQport(const PGconn *conn);
 extern char *PQtty(const PGconn *conn);
 extern char *PQoptions(const PGconn *conn);
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 975ab33d02..66fd317b94 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -397,6 +397,7 @@ struct pg_conn
 	int			nconnhost;		/* # of hosts named in conn string */
 	int			whichhost;		/* host we're currently trying/connected to */
 	pg_conn_host *connhost;		/* details about each named host */
+	char	   *connip;			/* IP address for current network connection */
 
 	/* Connection data */
 	pgsocket	sock;			/* FD for socket, PGINVALID_SOCKET if
#2Pavel Stehule
pavel.stehule@gmail.com
In reply to: Fabien COELHO (#1)
1 attachment(s)
Re: fix psql \conninfo & \connect when using hostaddr

Hi

pá 26. 10. 2018 v 15:55 odesílatel Fabien COELHO <coelho@cri.ensmp.fr>
napsal:

Hello,

This is a follow-up to another patch I posted about libpq confusing
documentation & psql resulting behavior under host/hostaddr settings.

Although the first mostly documentation patch did not gather much
enthousiasm, I still think both issues deserve a fix.

About updating psql's behavior, without this patch:

sh> psql "host=foo hostaddr=127.0.0.1"

psql> \conninfo
You are connected to database "fabien" as user "fabien" on host "foo"
at port "5432".
# NOPE, I'm really connected to localhost, foo does not even exist
# Other apparent inconsistencies are possible when hostaddr overrides
# "host" which is an socket directory or an IP.

psql> \c template1
could not translate host name "foo" to address: Name or service not
known
Previous connection kept
# hmmm.... what is the meaning of reusing a connection?
# this issue was pointed out by Arthur Zakirov

After the patch:

sh> psql "host=foo hostaddr=127.0.0.1"

psql> \conninfo
You are connected to database "fabien" as user "fabien" on host "foo"
(address "127.0.0.1") at port "5432".
# better

psql> \c template1
You are now connected to database "template1" as user "fabien".
# thanks

The patch adds a PQhostaddr() function to libpq which reports the
"hostaddr" setting or the current server ip. The function is used by psql
for \conninfo and when reusing parameters for \connect.

The messages are slightly more verbose because the IP is output. I think
that user asking for conninfo should survive to the more precise data.
This also comes handy if a host name resolves to several IPs (eg IPv6 and
IPv4, or several IPs...).

I checked this patch, and it looks well. The documentation is correct, all
tests passed. It does what is proposed.

I think so some redundant messages can be reduced - see function
printConnInfo - attached patch

If there are no be a objection, I'll mark this patch as ready for commiters

Regards

Pavel

Show quoted text

--
Fabien.

Attachments:

libpq-conn-2.patchtext/x-patch; charset=US-ASCII; name=libpq-conn-2.patchDownload
diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index 601091c570..a7c9f2b400 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -1735,6 +1735,35 @@ char *PQhost(const PGconn *conn);
      </listitem>
     </varlistentry>
 
+
+    <varlistentry id="libpq-pqhostaddr">
+     <term>
+      <function>PQhostaddr</function>
+      <indexterm>
+       <primary>PQhostaddr</primary>
+      </indexterm>
+     </term>
+
+     <listitem>
+      <para>
+       Returns the actual server IP address of the active connection.
+       This can be the address a host name resolved to, or an IP address
+       provided through the <literal>hostaddr</literal> parameter.
+<synopsis>
+char *PQhostaddr(const PGconn *conn);
+</synopsis>
+      </para>
+
+      <para>
+       <function>PQhostaddr</function> returns <symbol>NULL</symbol> if the
+       <parameter>conn</parameter> argument is <symbol>NULL</symbol>.
+       Otherwise, if there is an error producing the host information (perhaps
+       if the connection has not been fully established or there was an
+       error), it returns an empty string.
+      </para>
+     </listitem>
+    </varlistentry>
+
     <varlistentry id="libpq-pqport">
      <term>
       <function>PQport</function>
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 0dea54d3ce..5b361c9484 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -158,6 +158,7 @@ static void print_with_linenumbers(FILE *output, char *lines,
 					   const char *header_keyword);
 static void minimal_error_message(PGresult *res);
 
+static void printConnInfo(void);
 static void printSSLInfo(void);
 static bool printPsetInfo(const char *param, struct printQueryOpt *popt);
 static char *pset_value_string(const char *param, struct printQueryOpt *popt);
@@ -595,15 +596,7 @@ exec_command_conninfo(PsqlScanState scan_state, bool active_branch)
 			printf(_("You are currently not connected to a database.\n"));
 		else
 		{
-			char	   *host = PQhost(pset.db);
-
-			/* If the host is an absolute path, the connection is via socket */
-			if (is_absolute_path(host))
-				printf(_("You are connected to database \"%s\" as user \"%s\" via socket in \"%s\" at port \"%s\".\n"),
-					   db, PQuser(pset.db), host, PQport(pset.db));
-			else
-				printf(_("You are connected to database \"%s\" as user \"%s\" on host \"%s\" at port \"%s\".\n"),
-					   db, PQuser(pset.db), host, PQport(pset.db));
+			printConnInfo();
 			printSSLInfo();
 		}
 	}
@@ -2854,6 +2847,7 @@ do_connect(enum trivalue reuse_previous_specification,
 	PGconn	   *o_conn = pset.db,
 			   *n_conn;
 	char	   *password = NULL;
+	char	   *hostaddr = NULL;
 	bool		keep_password;
 	bool		has_connection_string;
 	bool		reuse_previous;
@@ -2894,12 +2888,22 @@ do_connect(enum trivalue reuse_previous_specification,
 	}
 
 	/* grab missing values from the old connection */
-	if (!user && reuse_previous)
-		user = PQuser(o_conn);
-	if (!host && reuse_previous)
-		host = PQhost(o_conn);
-	if (!port && reuse_previous)
-		port = PQport(o_conn);
+	if (reuse_previous)
+	{
+		if (!user)
+			user = PQuser(o_conn);
+		if (host && strcmp(host, PQhost(o_conn)) == 0)
+			/* if we are targetting the same host, reuse its hostaddr for consistency */
+			hostaddr = PQhostaddr(o_conn);
+		if (!host)
+		{
+			host = PQhost(o_conn);
+			/* also set hostaddr for consistency */
+			hostaddr = PQhostaddr(o_conn);
+		}
+		if (!port)
+			port = PQport(o_conn);
+	}
 
 	/*
 	 * Any change in the parameters read above makes us discard the password.
@@ -2961,13 +2965,18 @@ do_connect(enum trivalue reuse_previous_specification,
 
 	while (true)
 	{
-#define PARAMS_ARRAY_SIZE	8
+#define PARAMS_ARRAY_SIZE	9
 		const char **keywords = pg_malloc(PARAMS_ARRAY_SIZE * sizeof(*keywords));
 		const char **values = pg_malloc(PARAMS_ARRAY_SIZE * sizeof(*values));
 		int			paramnum = -1;
 
 		keywords[++paramnum] = "host";
 		values[paramnum] = host;
+		if (hostaddr && *hostaddr)
+		{
+			keywords[++paramnum] = "hostaddr";
+			values[paramnum] = hostaddr;
+		}
 		keywords[++paramnum] = "port";
 		values[paramnum] = port;
 		keywords[++paramnum] = "user";
@@ -3070,15 +3079,7 @@ do_connect(enum trivalue reuse_previous_specification,
 			param_is_newly_set(PQhost(o_conn), PQhost(pset.db)) ||
 			param_is_newly_set(PQport(o_conn), PQport(pset.db)))
 		{
-			char	   *host = PQhost(pset.db);
-
-			/* If the host is an absolute path, the connection is via socket */
-			if (is_absolute_path(host))
-				printf(_("You are now connected to database \"%s\" as user \"%s\" via socket in \"%s\" at port \"%s\".\n"),
-					   PQdb(pset.db), PQuser(pset.db), host, PQport(pset.db));
-			else
-				printf(_("You are now connected to database \"%s\" as user \"%s\" on host \"%s\" at port \"%s\".\n"),
-					   PQdb(pset.db), PQuser(pset.db), host, PQport(pset.db));
+			printConnInfo();
 		}
 		else
 			printf(_("You are now connected to database \"%s\" as user \"%s\".\n"),
@@ -3138,6 +3139,35 @@ connection_warnings(bool in_startup)
 }
 
 
+/*
+ * printConnInfo
+ *
+ * Print information about current connection.
+ */
+static void
+printConnInfo(void)
+{
+	char	   *host = PQhost(pset.db);
+	char	   *hostaddr = PQhostaddr(pset.db);
+	char	   *db = PQdb(pset.db);
+
+	/* If the host is an absolute path, the connection is via socket unless overriden by hostaddr */
+	if (is_absolute_path(host))
+		if (hostaddr && *hostaddr)
+			printf(_("You are connected to database \"%s\" as user \"%s\" on address \"%s\" at port \"%s\".\n"),
+				   db, PQuser(pset.db), hostaddr, PQport(pset.db));
+		else
+			printf(_("You are connected to database \"%s\" as user \"%s\" via socket in \"%s\" at port \"%s\".\n"),
+				   db, PQuser(pset.db), host, PQport(pset.db));
+	else
+		if (hostaddr && *hostaddr && strcmp(host, hostaddr) != 0)
+			printf(_("You are connected to database \"%s\" as user \"%s\" on host \"%s\" (address \"%s\") at port \"%s\".\n"),
+				   db, PQuser(pset.db), host, hostaddr, PQport(pset.db));
+		else
+			printf(_("You are connected to database \"%s\" as user \"%s\" on host \"%s\" at port \"%s\".\n"),
+				   db, PQuser(pset.db), host, PQport(pset.db));
+}
+
 /*
  * printSSLInfo
  *
diff --git a/src/interfaces/libpq/exports.txt b/src/interfaces/libpq/exports.txt
index 4359fae30d..cc9ee9ce6b 100644
--- a/src/interfaces/libpq/exports.txt
+++ b/src/interfaces/libpq/exports.txt
@@ -173,3 +173,4 @@ PQsetErrorContextVisibility 170
 PQresultVerboseErrorMessage 171
 PQencryptPasswordConn     172
 PQresultMemorySize        173
+PQhostaddr                174
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index d001bc513d..8e3acaf05d 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -1471,6 +1471,34 @@ connectNoDelay(PGconn *conn)
 	return 1;
 }
 
+static void
+getHostaddr(PGconn *conn, char host_addr[NI_MAXHOST])
+{
+	struct sockaddr_storage *addr = &conn->raddr.addr;
+
+	if (conn->connhost[conn->whichhost].type == CHT_HOST_ADDRESS)
+		strlcpy(host_addr, conn->connhost[conn->whichhost].hostaddr, NI_MAXHOST);
+	else if (addr->ss_family == AF_INET)
+	{
+		if (inet_net_ntop(AF_INET,
+						  &((struct sockaddr_in *) addr)->sin_addr.s_addr,
+						  32,
+						  host_addr, NI_MAXHOST) == NULL)
+			strcpy(host_addr, "???");
+	}
+#ifdef HAVE_IPV6
+	else if (addr->ss_family == AF_INET6)
+	{
+		if (inet_net_ntop(AF_INET6,
+						  &((struct sockaddr_in6 *) addr)->sin6_addr.s6_addr,
+						  128,
+						  host_addr, NI_MAXHOST) == NULL)
+			strcpy(host_addr, "???");
+	}
+#endif
+	else
+		strcpy(host_addr, "???");
+}
 
 /* ----------
  * connectFailureMessage -
@@ -1504,34 +1532,12 @@ connectFailureMessage(PGconn *conn, int errorno)
 		char		host_addr[NI_MAXHOST];
 		const char *displayed_host;
 		const char *displayed_port;
-		struct sockaddr_storage *addr = &conn->raddr.addr;
 
 		/*
 		 * Optionally display the network address with the hostname. This is
 		 * useful to distinguish between IPv4 and IPv6 connections.
 		 */
-		if (conn->connhost[conn->whichhost].type == CHT_HOST_ADDRESS)
-			strlcpy(host_addr, conn->connhost[conn->whichhost].hostaddr, NI_MAXHOST);
-		else if (addr->ss_family == AF_INET)
-		{
-			if (inet_net_ntop(AF_INET,
-							  &((struct sockaddr_in *) addr)->sin_addr.s_addr,
-							  32,
-							  host_addr, sizeof(host_addr)) == NULL)
-				strcpy(host_addr, "???");
-		}
-#ifdef HAVE_IPV6
-		else if (addr->ss_family == AF_INET6)
-		{
-			if (inet_net_ntop(AF_INET6,
-							  &((struct sockaddr_in6 *) addr)->sin6_addr.s6_addr,
-							  128,
-							  host_addr, sizeof(host_addr)) == NULL)
-				strcpy(host_addr, "???");
-		}
-#endif
-		else
-			strcpy(host_addr, "???");
+		getHostaddr(conn, host_addr);
 
 		/* To which host and port were we actually connecting? */
 		if (conn->connhost[conn->whichhost].type == CHT_HOST_ADDRESS)
@@ -2302,6 +2308,21 @@ keep_going:						/* We will come back to here until there is
 						   addr_cur->ai_addrlen);
 					conn->raddr.salen = addr_cur->ai_addrlen;
 
+
+					/* set connip */
+					if (conn->connip != NULL)
+					{
+						free(conn->connip);
+						conn->connip = NULL;
+					}
+
+					{
+						char		host_addr[NI_MAXHOST];
+						getHostaddr(conn, host_addr);
+						if (strcmp(host_addr, "???") != 0)
+							conn->connip = strdup(host_addr);
+					}
+
 					conn->sock = socket(addr_cur->ai_family, SOCK_STREAM, 0);
 					if (conn->sock == PGINVALID_SOCKET)
 					{
@@ -6172,6 +6193,25 @@ PQhost(const PGconn *conn)
 	return "";
 }
 
+char *
+PQhostaddr(const PGconn *conn)
+{
+	if (!conn)
+		return NULL;
+
+	if (conn->connhost != NULL)
+	{
+		if (conn->connhost[conn->whichhost].hostaddr != NULL &&
+			conn->connhost[conn->whichhost].hostaddr[0] != '\0')
+			return conn->connhost[conn->whichhost].hostaddr;
+
+		if (conn->connip != NULL)
+			return conn->connip;
+	}
+
+	return "";
+}
+
 char *
 PQport(const PGconn *conn)
 {
diff --git a/src/interfaces/libpq/libpq-fe.h b/src/interfaces/libpq/libpq-fe.h
index 52bd5d2cd8..3f13ddf092 100644
--- a/src/interfaces/libpq/libpq-fe.h
+++ b/src/interfaces/libpq/libpq-fe.h
@@ -312,6 +312,7 @@ extern char *PQdb(const PGconn *conn);
 extern char *PQuser(const PGconn *conn);
 extern char *PQpass(const PGconn *conn);
 extern char *PQhost(const PGconn *conn);
+extern char *PQhostaddr(const PGconn *conn);
 extern char *PQport(const PGconn *conn);
 extern char *PQtty(const PGconn *conn);
 extern char *PQoptions(const PGconn *conn);
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 975ab33d02..66fd317b94 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -397,6 +397,7 @@ struct pg_conn
 	int			nconnhost;		/* # of hosts named in conn string */
 	int			whichhost;		/* host we're currently trying/connected to */
 	pg_conn_host *connhost;		/* details about each named host */
+	char	   *connip;			/* IP address for current network connection */
 
 	/* Connection data */
 	pgsocket	sock;			/* FD for socket, PGINVALID_SOCKET if
#3Arthur Zakirov
a.zakirov@postgrespro.ru
In reply to: Pavel Stehule (#2)
Re: fix psql \conninfo & \connect when using hostaddr

Hello all,

On 07.11.2018 16:23, Pavel Stehule wrote:

pá 26. 10. 2018 v 15:55 odesílatel Fabien COELHO <coelho@cri.ensmp.fr
<mailto:coelho@cri.ensmp.fr>> napsal:

This is a follow-up to another patch I posted about libpq confusing
documentation & psql resulting behavior under host/hostaddr settings.

Although the first mostly documentation patch did not gather much
enthousiasm, I still think both issues deserve a fix.

I checked this patch, and it looks well. The documentation is correct,
all tests passed. It does what is proposed.

I think so some redundant messages can be reduced - see function
printConnInfo - attached patch

I have few notes about patches.

/* If the host is an absolute path, the connection is via socket unless overriden by hostaddr */
if (is_absolute_path(host))
if (hostaddr && *hostaddr)
printf(_("You are connected to database \"%s\" as user \"%s\" on address \"%s\" at port \"%s\".\n"),
db, PQuser(pset.db), hostaddr, PQport(pset.db));
else
printf(_("You are connected to database \"%s\" as user \"%s\" via socket in \"%s\" at port \"%s\".\n"),
db, PQuser(pset.db), host, PQport(pset.db));
else
if (hostaddr && *hostaddr && strcmp(host, hostaddr) != 0)
printf(_("You are connected to database \"%s\" as user \"%s\" on host \"%s\" (address \"%s\") at port \"%s\".\n"),
db, PQuser(pset.db), host, hostaddr, PQport(pset.db));
else
printf(_("You are connected to database \"%s\" as user \"%s\" on host \"%s\" at port \"%s\".\n"),
db, PQuser(pset.db), host, PQport(pset.db));

I think there is lack of necessary braces here for first if and second
else branches. This is true for both patches.

/* set connip */
if (conn->connip != NULL)
{
free(conn->connip);
conn->connip = NULL;
}

{
char host_addr[NI_MAXHOST];
getHostaddr(conn, host_addr);
if (strcmp(host_addr, "???") != 0)
conn->connip = strdup(host_addr);
}

Maybe it is better to move this code into the PQhostaddr() function?
Since connip is used only in PQhostaddr() it might be not worth to do
additional work in main PQconnectPoll().

--
Arthur Zakirov
Postgres Professional: http://www.postgrespro.com
Russian Postgres Company

#4Pavel Stehule
pavel.stehule@gmail.com
In reply to: Arthur Zakirov (#3)
Re: fix psql \conninfo & \connect when using hostaddr

st 7. 11. 2018 v 15:11 odesílatel Arthur Zakirov <a.zakirov@postgrespro.ru>
napsal:

Hello all,

On 07.11.2018 16:23, Pavel Stehule wrote:

pá 26. 10. 2018 v 15:55 odesílatel Fabien COELHO <coelho@cri.ensmp.fr
<mailto:coelho@cri.ensmp.fr>> napsal:

This is a follow-up to another patch I posted about libpq confusing
documentation & psql resulting behavior under host/hostaddr

settings.

Although the first mostly documentation patch did not gather much
enthousiasm, I still think both issues deserve a fix.

I checked this patch, and it looks well. The documentation is correct,
all tests passed. It does what is proposed.

I think so some redundant messages can be reduced - see function
printConnInfo - attached patch

I have few notes about patches.

/* If the host is an absolute path, the connection is via socket unless

overriden by hostaddr */

if (is_absolute_path(host))
if (hostaddr && *hostaddr)
printf(_("You are connected to database \"%s\" as user

\"%s\" on address \"%s\" at port \"%s\".\n"),

db, PQuser(pset.db), hostaddr, PQport(pset.db));
else
printf(_("You are connected to database \"%s\" as user

\"%s\" via socket in \"%s\" at port \"%s\".\n"),

db, PQuser(pset.db), host, PQport(pset.db));
else
if (hostaddr && *hostaddr && strcmp(host, hostaddr) != 0)
printf(_("You are connected to database \"%s\" as user

\"%s\" on host \"%s\" (address \"%s\") at port \"%s\".\n"),

db, PQuser(pset.db), host, hostaddr,

PQport(pset.db));

else
printf(_("You are connected to database \"%s\" as user

\"%s\" on host \"%s\" at port \"%s\".\n"),

db, PQuser(pset.db), host, PQport(pset.db));

I think there is lack of necessary braces here for first if and second
else branches. This is true for both patches.

?

Pavel

Show quoted text

/* set connip */
if (conn->connip != NULL)
{
free(conn->connip);
conn->connip = NULL;
}

{
char host_addr[NI_MAXHOST];
getHostaddr(conn, host_addr);
if (strcmp(host_addr, "???") != 0)
conn->connip = strdup(host_addr);
}

Maybe it is better to move this code into the PQhostaddr() function?
Since connip is used only in PQhostaddr() it might be not worth to do
additional work in main PQconnectPoll().

--
Arthur Zakirov
Postgres Professional: http://www.postgrespro.com
Russian Postgres Company

#5Robert Haas
robertmhaas@gmail.com
In reply to: Fabien COELHO (#1)
Re: fix psql \conninfo & \connect when using hostaddr

On Fri, Oct 26, 2018 at 9:54 AM Fabien COELHO <coelho@cri.ensmp.fr> wrote:

About updating psql's behavior, without this patch:

sh> psql "host=foo hostaddr=127.0.0.1"

psql> \conninfo
You are connected to database "fabien" as user "fabien" on host "foo" at port "5432".
# NOPE, I'm really connected to localhost, foo does not even exist
# Other apparent inconsistencies are possible when hostaddr overrides
# "host" which is an socket directory or an IP.

I remain of the opinion that this is not a bug. You told it that foo
has address 127.0.0.1 and it believed you; that's YOUR fault.

After the patch:

sh> psql "host=foo hostaddr=127.0.0.1"

psql> \conninfo
You are connected to database "fabien" as user "fabien" on host "foo" (address "127.0.0.1") at port "5432".
# better

Nevertheless, that seems like a reasonable change to the output. Will
your patch show the IP address in all cases or only when hostaddr is
specified?

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#6Arthur Zakirov
a.zakirov@postgrespro.ru
In reply to: Pavel Stehule (#4)
Re: fix psql \conninfo & \connect when using hostaddr

On 07.11.2018 20:11, Pavel Stehule wrote:

st 7. 11. 2018 v 15:11 odesílatel Arthur Zakirov
<a.zakirov@postgrespro.ru <mailto:a.zakirov@postgrespro.ru>> napsal:

I think there is lack of necessary braces here for first if and second
else branches. This is true for both patches.

?

I just meant something like this (additional "{", "}" braces):

if (is_absolute_path(host))
{
...
}
else
{
...
}

--
Arthur Zakirov
Postgres Professional: http://www.postgrespro.com
Russian Postgres Company

#7Fabien COELHO
coelho@cri.ensmp.fr
In reply to: Robert Haas (#5)
Re: fix psql \conninfo & \connect when using hostaddr

Hello Robert,

psql> \conninfo
You are connected to database "fabien" as user "fabien" on host "foo" at port "5432".

I remain of the opinion that this is not a bug. You told it that foo
has address 127.0.0.1 and it believed you; that's YOUR fault.

Hmmm. For me, if a user asks \conninfo for connection information, they
expect to be told what the connection actually is, regardless of the
initial connection string.

Another more stricking instance:

sh> psql "host=/tmp port=5432 hostaddr=127.0.0.1"
...
fabien=# \conninfo
You are connected to database "fabien" as user "fabien" via socket in "/tmp" at port "5432".
SSL connection (protocol: TLSv1.2, cipher: ECDHE-RSA-AES256-GCM-SHA384, bits: 256, compression: off)

It says that there is a socket, but there is none. The SSL bit is a
giveaway, there is no SSL on Unix-domain sockets.

sh> psql "host=foo hostaddr=127.0.0.1"

psql> \conninfo
You are connected to database "fabien" as user "fabien" on host "foo" (address "127.0.0.1") at port "5432".

Nevertheless, that seems like a reasonable change to the output. Will
your patch show the IP address in all cases or only when hostaddr is
specified?

It is always printed, unless both host & address are equal.

The rational is that it is also potentially useful for multi-ip dns
resolutions, and generating a valid hostaddr allows \connect defaults to
reuse the actual same connection, including the IP that was chosen.

Also, the added information is quite short, and if a user explicitely asks
for connection information, I think they can handle the slightly expanded
answer.

--
Fabien.

#8Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Arthur Zakirov (#6)
Re: fix psql \conninfo & \connect when using hostaddr

On 2018-Nov-08, Arthur Zakirov wrote:

On 07.11.2018 20:11, Pavel Stehule wrote:

st 7. 11. 2018 v�15:11 odes�latel Arthur Zakirov
<a.zakirov@postgrespro.ru <mailto:a.zakirov@postgrespro.ru>> napsal:

I think there is lack of necessary braces here for first if and second
else branches. This is true for both patches.

?

I just meant something like this (additional "{", "}" braces):

We omit braces when there's an individual statement. (We do add the
braces when we have a comment atop the individual statement, though, to
avoid pgindent from doing a stupid thing.)

--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#9Tom Lane
tgl@sss.pgh.pa.us
In reply to: Alvaro Herrera (#8)
Re: fix psql \conninfo & \connect when using hostaddr

Alvaro Herrera <alvherre@2ndquadrant.com> writes:

On 2018-Nov-08, Arthur Zakirov wrote:

I just meant something like this (additional "{", "}" braces):

We omit braces when there's an individual statement. (We do add the
braces when we have a comment atop the individual statement, though, to
avoid pgindent from doing a stupid thing.)

For the record --- I just checked, and pgindent will not mess up code like

if (condition)
/* comment here */
do_something();

at least not as long as the comment is short enough for one line.
(If it's a multiline comment, it seems to want to put a blank line
in front of it, which is not very nice in this context.)

Visually, however, I think this is better off with braces because
it *looks* like a multi-line if-block. The braces also make it
clear that your intent was not, say,

while (some-mutable-condition)
/* skip */ ;
do_something_else();

regards, tom lane

#10Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Tom Lane (#9)
Re: fix psql \conninfo & \connect when using hostaddr

On 2018-Nov-08, Tom Lane wrote:

For the record --- I just checked, and pgindent will not mess up code like

if (condition)
/* comment here */
do_something();

at least not as long as the comment is short enough for one line.
(If it's a multiline comment, it seems to want to put a blank line
in front of it, which is not very nice in this context.)

Yeah, those blank lines are what I've noticed, and IMO they look pretty
bad.

Visually, however, I think this is better off with braces because
it *looks* like a multi-line if-block. The braces also make it
clear that your intent was not, say,

while (some-mutable-condition)
/* skip */ ;
do_something_else();

Right, that too. Fortunately I think compilers warn about mismatching
indentation nowadays, at least in some cases.

--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#11Michael Paquier
michael@paquier.xyz
In reply to: Alvaro Herrera (#10)
Re: fix psql \conninfo & \connect when using hostaddr

On Thu, Nov 08, 2018 at 12:13:31PM -0300, Alvaro Herrera wrote:

On 2018-Nov-08, Tom Lane wrote:

Visually, however, I think this is better off with braces because
it *looks* like a multi-line if-block. The braces also make it
clear that your intent was not, say,

while (some-mutable-condition)
/* skip */ ;
do_something_else();

Right, that too. Fortunately I think compilers warn about mismatching
indentation nowadays, at least in some cases.

I don't recall seeing a compiler warning about that, but I do recall
coverity complaining loudly about such things. It is better style to
use braces if there is one line of code with a comment block in my
opinion. And there is no need for braces if there is no comment.
--
Michael

#12Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Fabien COELHO (#1)
Re: fix psql \conninfo & \connect when using hostaddr

Looks good to me, save that I would change the API of getHostaddr() to
this:

/* ----------
* getHostaddr -
* Fills 'host_addr' with the string representation of the currently connected
* socket in 'conn'.
* ----------
*/
static void
getHostaddr(PGconn *conn, char *host_addr, int host_addr_len)

(Trying to pass arrays as such to C functions is a lost cause -- just a
documentation aid at best, and a source of confusion and crashes at
worst.)

I tried to see about overflowing the NI_MAXHOST length with a long
socket dir, but that doesn't actually work, though the first line of
error here is a bit surprising:

LC_ALL=C psql "host="\'"`pwd`"\'""
could not identify current directory: Numerical result out of range
psql: Unix-domain socket path "/tmp/En un lugar de la Mancha_ de cuyo nombre no quiero acordarme_ no ha mucho tiempo que vivía un hidalgo de los de lanza en astillero_ adarga antigua_ rocín flaco y galgo corredor./Una olla de algo más vaca que carnero_ salpicón las más noches_ duelos y quebrantos los sábados_ lentejas los viernes_ algún palomino de añadidura los domingos_ consumían las tres partes de su hacienda./El resto della concluían sayo de velarte_ calzas de velludo para las fiestas con sus pantuflos de lo mismo_ los días de entre semana se honraba con su vellori de lo más fino./Tenía en su casa una ama que pasaba de los cuarenta_ y una sobrina que no llegaba a los veinte_ y un mozo de campo y plaza_ que así ensillaba el rocín como tomaba la podadera./Frisaba la edad de nuestro hidalgo con los cincuenta años_ era de complexión recia_ seco de carnes_ enjuto de rostro; gran madrugador y amigo de la caza./Quieren decir que tenía el sobrenombre de Quijada o Quesada (que en esto hay alguna diferencia en los autores que " is too long (maximum 107 bytes)

This is after I replaced all the , to _, because the original was even
more fun:

LC_ALL=C psql "host="\'"`pwd`"\'""
could not identify current directory: Numerical result out of range
psql: could not connect to server: No such file or directory
Is the server running locally and accepting
connections on Unix domain socket "/tmp/En un lugar de la Mancha/.s.PGSQL.55432"?
could not translate host name " de cuyo nombre no quiero acordarme" to address: Name or service not known
could not translate host name " no ha mucho tiempo que vivía un hidalgo de los de lanza en astillero" to address: Name or service not known
could not translate host name " adarga antigua" to address: Name or service not known
could not translate host name " rocín flaco y galgo corredor./Una olla de algo más vaca que carnero" to address: Name or service not known
could not translate host name " salpicón las más noches" to address: Name or service not known
could not translate host name " duelos y quebrantos los sábados" to address: Name or service not known
could not translate host name " lentejas los viernes" to address: Name or service not known
could not translate host name " algún palomino de añadidura los domingos" to address: Name or service not known
could not translate host name " consumían las tres partes de su hacienda./El resto della concluían sayo de velarte" to address: Name or service not known
could not translate host name " calzas de velludo para las fiestas con sus pantuflos de lo mismo" to address: Name or service not known
could not translate host name " los días de entre semana se honraba con su vellori de lo más fino./Tenía en su casa una ama que pasaba de los cuarenta" to address: Name or service not known
could not translate host name " y una sobrina que no llegaba a los veinte" to address: Name or service not known
could not translate host name " y un mozo de campo y plaza" to address: Name or service not known
could not translate host name " que así ensillaba el rocín como tomaba la podadera./Frisaba la edad de nuestro hidalgo con los cincuenta años" to address: Name or service not known
could not translate host name " era de complexión recia" to address: Name or service not known
could not translate host name " seco de carnes" to address: Name or service not known
could not translate host name " enjuto de rostro; gran madrugador y amigo de la caza./Quieren decir que tenía el sobrenombre de Quijada o Quesada (que en esto hay alguna diferencia en los autores que deste caso escriben)" to address: Name or service not known
could not translate host name " aunque por conjeturas verosímiles se deja entender que se llama Quijana;/pero esto importa poco a nuestro cuento; basta que en la narración dél no se salga un punto de la verdad." to address: Name or service not known

Funky test cases aside, I verified that giving hostaddr behaves better
with the patch. This is unpatched:

$ LC_ALL=C psql "host="\'"`pwd`"\'" hostaddr=::1"
psql (12devel, server 11.1)
Type "help" for help.

55442 12devel 879890=# \conninfo
You are connected to database "alvherre" as user "alvherre" via socket in "/tmp/En un lugar de la Mancha_ de cuyo nombre no quiero acordarme_ no ha mucho tiempo" at port "55442".

and this is patched:

$ LC_ALL=C psql "host="\'"`pwd`"\'" hostaddr=::1"
psql (12devel, server 11.1)
Type "help" for help.

55442 12devel 881754=# \conninfo
You are connected to database "alvherre" as user "alvherre" on address "::1" at port "55442".

--
Álvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#13Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Alvaro Herrera (#12)
Re: fix psql \conninfo & \connect when using hostaddr

On 2018-Nov-17, Alvaro Herrera wrote:

Looks good to me, save that I would change the API of getHostaddr() to
this:

/* ----------
* getHostaddr -
* Fills 'host_addr' with the string representation of the currently connected
* socket in 'conn'.
* ----------
*/
static void
getHostaddr(PGconn *conn, char *host_addr, int host_addr_len)

Fabien, are you planning to fix things per Arthur's review?

--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#14Fabien COELHO
coelho@cri.ensmp.fr
In reply to: Alvaro Herrera (#13)
Re: fix psql \conninfo & \connect when using hostaddr

On Sat, 17 Nov 2018, Alvaro Herrera wrote:

/* ----------
* getHostaddr -
* Fills 'host_addr' with the string representation of the currently connected
* socket in 'conn'.
* ----------
*/
static void
getHostaddr(PGconn *conn, char *host_addr, int host_addr_len)

Fabien, are you planning to fix things per Arthur's review?

Yep, I am.

I will not do the above though, because the PQgetHostaddr API would differ
from all other connection status functions (PQgetHost, PQgetUser...) which
are all "char * PQget<Something>(PGconn * conn)"

--
Fabien.

#15Fabien COELHO
coelho@cri.ensmp.fr
In reply to: Fabien COELHO (#14)
Re: fix psql \conninfo & \connect when using hostaddr

Fabien, are you planning to fix things per Arthur's review?

Yep, I am.

I will not do the above though, because the PQgetHostaddr API would differ
from all other connection status functions (PQgetHost, PQgetUser...) which
are all "char * PQget<Something>(PGconn * conn)"

Sorry, I'm mixing everything, the function is an internal one.

I'm working on improving the patch.

--
Fabien.

#16Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Fabien COELHO (#15)
Re: fix psql \conninfo & \connect when using hostaddr

On 2018-Nov-17, Fabien COELHO wrote:

Fabien, are you planning to fix things per Arthur's review?

Yep, I am.

I will not do the above though, because the PQgetHostaddr API would
differ from all other connection status functions (PQgetHost,
PQgetUser...) which are all "char * PQget<Something>(PGconn * conn)"

Sorry, I'm mixing everything, the function is an internal one.

Yeah :-)

I'm working on improving the patch.

Cool.

--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#17Fabien COELHO
coelho@cri.ensmp.fr
In reply to: Pavel Stehule (#2)
Re: fix psql \conninfo & \connect when using hostaddr

Hello Pavel,

I think so some redundant messages can be reduced - see function
printConnInfo - attached patch

I thought about doing like that, but I made the debatable choice to keep
the existing redundancy because it minimizes diffs and having a
print-to-stdout special function does not look like a very clean API, as
it cannot really be used by non CLI clients.

--
Fabien.

#18Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Fabien COELHO (#17)
Re: fix psql \conninfo & \connect when using hostaddr

On 2018-Nov-17, Fabien COELHO wrote:

I think so some redundant messages can be reduced - see function
printConnInfo - attached patch

I thought about doing like that, but I made the debatable choice to keep the
existing redundancy because it minimizes diffs and having a print-to-stdout
special function does not look like a very clean API, as it cannot really be
used by non CLI clients.

What? This is psql, so it doesn't affect non-CLI clientes, does it?

On the other hand, one message says "you're NOW connected", the other
doesn't have the "now". If we're dropping the "now" (I think it's
useless), let's make an explicit choice about it. TBH I'd drop the
"you're" also, so both \conninfo and \c would say

Connected to database foo <conn details>

Anyway, a trivial change that's sure to make bikeshed paint seller cry
with so many customers yelling at each other; not for this patch.

--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#19Fabien COELHO
fabien.coelho@mines-paristech.fr
In reply to: Alvaro Herrera (#18)
Re: fix psql \conninfo & \connect when using hostaddr

I think so some redundant messages can be reduced - see function
printConnInfo - attached patch

I thought about doing like that, but I made the debatable choice to keep the
existing redundancy because it minimizes diffs and having a print-to-stdout
special function does not look like a very clean API, as it cannot really be
used by non CLI clients.

What? This is psql, so it doesn't affect non-CLI clientes, does it?

Indeed, you are right, and I'm really mixing everything today. What I
really thought was to have a function which would return the full
description.

On the other hand, one message says "you're NOW connected",

Indeed, the text is slightly different.

the other doesn't have the "now". If we're dropping the "now" (I think
it's useless), let's make an explicit choice about it. TBH I'd drop the
"you're" also, so both \conninfo and \c would say

Connected to database foo <conn details>

Anyway, a trivial change that's sure to make bikeshed paint seller cry
with so many customers yelling at each other; not for this patch.

Ok. I'm not planning to refactor "psql" today.

--
Fabien.

#20Fabien COELHO
coelho@cri.ensmp.fr
In reply to: Alvaro Herrera (#16)
1 attachment(s)
Re: fix psql \conninfo & \connect when using hostaddr

I'm working on improving the patch.

Cool.

Here is the updated v2

- libpq internal function getHostaddr get a length,
and I added an assert about it.
- added a few braces on if/if/else/if/else/else
- added an UNKNOWN_HOST macro to hide "???"
- moved host_addr[] declaration earlier to avoid some braces
- I have not refactored psql connection message,
but finally agree with Pavel & you have a point.

--
Fabien.

Attachments:

libpq-conn-2.patchtext/x-diff; name=libpq-conn-2.patchDownload
diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index 601091c570..a7c9f2b400 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -1735,6 +1735,35 @@ char *PQhost(const PGconn *conn);
      </listitem>
     </varlistentry>
 
+
+    <varlistentry id="libpq-pqhostaddr">
+     <term>
+      <function>PQhostaddr</function>
+      <indexterm>
+       <primary>PQhostaddr</primary>
+      </indexterm>
+     </term>
+
+     <listitem>
+      <para>
+       Returns the actual server IP address of the active connection.
+       This can be the address a host name resolved to, or an IP address
+       provided through the <literal>hostaddr</literal> parameter.
+<synopsis>
+char *PQhostaddr(const PGconn *conn);
+</synopsis>
+      </para>
+
+      <para>
+       <function>PQhostaddr</function> returns <symbol>NULL</symbol> if the
+       <parameter>conn</parameter> argument is <symbol>NULL</symbol>.
+       Otherwise, if there is an error producing the host information (perhaps
+       if the connection has not been fully established or there was an
+       error), it returns an empty string.
+      </para>
+     </listitem>
+    </varlistentry>
+
     <varlistentry id="libpq-pqport">
      <term>
       <function>PQport</function>
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 04e227b5a6..c28ef8fe36 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -596,14 +596,27 @@ exec_command_conninfo(PsqlScanState scan_state, bool active_branch)
 		else
 		{
 			char	   *host = PQhost(pset.db);
+			char	   *hostaddr = PQhostaddr(pset.db);
 
-			/* If the host is an absolute path, the connection is via socket */
+			/* If the host is an absolute path, the connection is via socket unless overriden by hostaddr */
 			if (is_absolute_path(host))
-				printf(_("You are connected to database \"%s\" as user \"%s\" via socket in \"%s\" at port \"%s\".\n"),
-					   db, PQuser(pset.db), host, PQport(pset.db));
+			{
+				if (hostaddr && *hostaddr)
+					printf(_("You are connected to database \"%s\" as user \"%s\" on address \"%s\" at port \"%s\".\n"),
+						   db, PQuser(pset.db), hostaddr, PQport(pset.db));
+				else
+					printf(_("You are connected to database \"%s\" as user \"%s\" via socket in \"%s\" at port \"%s\".\n"),
+						   db, PQuser(pset.db), host, PQport(pset.db));
+			}
 			else
-				printf(_("You are connected to database \"%s\" as user \"%s\" on host \"%s\" at port \"%s\".\n"),
-					   db, PQuser(pset.db), host, PQport(pset.db));
+			{
+				if (hostaddr && *hostaddr && strcmp(host, hostaddr) != 0)
+					printf(_("You are connected to database \"%s\" as user \"%s\" on host \"%s\" (address \"%s\") at port \"%s\".\n"),
+						   db, PQuser(pset.db), host, hostaddr, PQport(pset.db));
+				else
+					printf(_("You are connected to database \"%s\" as user \"%s\" on host \"%s\" at port \"%s\".\n"),
+						   db, PQuser(pset.db), host, PQport(pset.db));
+			}
 			printSSLInfo();
 		}
 	}
@@ -2854,6 +2867,7 @@ do_connect(enum trivalue reuse_previous_specification,
 	PGconn	   *o_conn = pset.db,
 			   *n_conn;
 	char	   *password = NULL;
+	char	   *hostaddr = NULL;
 	bool		keep_password;
 	bool		has_connection_string;
 	bool		reuse_previous;
@@ -2894,12 +2908,22 @@ do_connect(enum trivalue reuse_previous_specification,
 	}
 
 	/* grab missing values from the old connection */
-	if (!user && reuse_previous)
-		user = PQuser(o_conn);
-	if (!host && reuse_previous)
-		host = PQhost(o_conn);
-	if (!port && reuse_previous)
-		port = PQport(o_conn);
+	if (reuse_previous)
+	{
+		if (!user)
+			user = PQuser(o_conn);
+		if (host && strcmp(host, PQhost(o_conn)) == 0)
+			/* if we are targetting the same host, reuse its hostaddr for consistency */
+			hostaddr = PQhostaddr(o_conn);
+		if (!host)
+		{
+			host = PQhost(o_conn);
+			/* also set hostaddr for consistency */
+			hostaddr = PQhostaddr(o_conn);
+		}
+		if (!port)
+			port = PQport(o_conn);
+	}
 
 	/*
 	 * Any change in the parameters read above makes us discard the password.
@@ -2961,13 +2985,18 @@ do_connect(enum trivalue reuse_previous_specification,
 
 	while (true)
 	{
-#define PARAMS_ARRAY_SIZE	8
+#define PARAMS_ARRAY_SIZE	9
 		const char **keywords = pg_malloc(PARAMS_ARRAY_SIZE * sizeof(*keywords));
 		const char **values = pg_malloc(PARAMS_ARRAY_SIZE * sizeof(*values));
 		int			paramnum = -1;
 
 		keywords[++paramnum] = "host";
 		values[paramnum] = host;
+		if (hostaddr && *hostaddr)
+		{
+			keywords[++paramnum] = "hostaddr";
+			values[paramnum] = hostaddr;
+		}
 		keywords[++paramnum] = "port";
 		values[paramnum] = port;
 		keywords[++paramnum] = "user";
@@ -3071,14 +3100,27 @@ do_connect(enum trivalue reuse_previous_specification,
 			param_is_newly_set(PQport(o_conn), PQport(pset.db)))
 		{
 			char	   *host = PQhost(pset.db);
+			char	   *hostaddr = PQhostaddr(pset.db);
 
 			/* If the host is an absolute path, the connection is via socket */
 			if (is_absolute_path(host))
-				printf(_("You are now connected to database \"%s\" as user \"%s\" via socket in \"%s\" at port \"%s\".\n"),
-					   PQdb(pset.db), PQuser(pset.db), host, PQport(pset.db));
+			{
+				if (hostaddr && *hostaddr)
+					printf(_("You are now connected to database \"%s\" as user \"%s\" on address \"%s\" at port \"%s\".\n"),
+						   PQdb(pset.db), PQuser(pset.db), hostaddr, PQport(pset.db));
+				else
+					printf(_("You are now connected to database \"%s\" as user \"%s\" via socket in \"%s\" at port \"%s\".\n"),
+						   PQdb(pset.db), PQuser(pset.db), host, PQport(pset.db));
+			}
 			else
-				printf(_("You are now connected to database \"%s\" as user \"%s\" on host \"%s\" at port \"%s\".\n"),
-					   PQdb(pset.db), PQuser(pset.db), host, PQport(pset.db));
+			{
+				if (hostaddr && *hostaddr && strcmp(host, hostaddr) != 0)
+					printf(_("You are now connected to database \"%s\" as user \"%s\" on host \"%s\" (address \"%s\") at port \"%s\".\n"),
+						   PQdb(pset.db), PQuser(pset.db), host, hostaddr, PQport(pset.db));
+				else
+					printf(_("You are now connected to database \"%s\" as user \"%s\" on host \"%s\" at port \"%s\".\n"),
+						   PQdb(pset.db), PQuser(pset.db), host, PQport(pset.db));
+			}
 		}
 		else
 			printf(_("You are now connected to database \"%s\" as user \"%s\".\n"),
diff --git a/src/interfaces/libpq/exports.txt b/src/interfaces/libpq/exports.txt
index 4359fae30d..cc9ee9ce6b 100644
--- a/src/interfaces/libpq/exports.txt
+++ b/src/interfaces/libpq/exports.txt
@@ -173,3 +173,4 @@ PQsetErrorContextVisibility 170
 PQresultVerboseErrorMessage 171
 PQencryptPasswordConn     172
 PQresultMemorySize        173
+PQhostaddr                174
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index d001bc513d..f77a6a821f 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -1471,6 +1471,40 @@ connectNoDelay(PGconn *conn)
 	return 1;
 }
 
+#define UNKNOWN_HOST "???"
+
+/* generate host address as a string, or write "???"
+ */
+static void
+getHostaddr(PGconn *conn, char *host_addr, int host_addr_len)
+{
+	struct sockaddr_storage *addr = &conn->raddr.addr;
+
+	Assert(host_addr != NULL && host_addr_len > strlen(UNKNOWN_HOST));
+
+	if (conn->connhost[conn->whichhost].type == CHT_HOST_ADDRESS)
+		strlcpy(host_addr, conn->connhost[conn->whichhost].hostaddr, host_addr_len);
+	else if (addr->ss_family == AF_INET)
+	{
+		if (inet_net_ntop(AF_INET,
+						  &((struct sockaddr_in *) addr)->sin_addr.s_addr,
+						  32,
+						  host_addr, host_addr_len) == NULL)
+			strcpy(host_addr, UNKNOWN_HOST);
+	}
+#ifdef HAVE_IPV6
+	else if (addr->ss_family == AF_INET6)
+	{
+		if (inet_net_ntop(AF_INET6,
+						  &((struct sockaddr_in6 *) addr)->sin6_addr.s6_addr,
+						  128,
+						  host_addr, host_addr_len) == NULL)
+			strcpy(host_addr, UNKNOWN_HOST);
+	}
+#endif
+	else
+		strcpy(host_addr, UNKNOWN_HOST);
+}
 
 /* ----------
  * connectFailureMessage -
@@ -1504,34 +1538,12 @@ connectFailureMessage(PGconn *conn, int errorno)
 		char		host_addr[NI_MAXHOST];
 		const char *displayed_host;
 		const char *displayed_port;
-		struct sockaddr_storage *addr = &conn->raddr.addr;
 
 		/*
 		 * Optionally display the network address with the hostname. This is
 		 * useful to distinguish between IPv4 and IPv6 connections.
 		 */
-		if (conn->connhost[conn->whichhost].type == CHT_HOST_ADDRESS)
-			strlcpy(host_addr, conn->connhost[conn->whichhost].hostaddr, NI_MAXHOST);
-		else if (addr->ss_family == AF_INET)
-		{
-			if (inet_net_ntop(AF_INET,
-							  &((struct sockaddr_in *) addr)->sin_addr.s_addr,
-							  32,
-							  host_addr, sizeof(host_addr)) == NULL)
-				strcpy(host_addr, "???");
-		}
-#ifdef HAVE_IPV6
-		else if (addr->ss_family == AF_INET6)
-		{
-			if (inet_net_ntop(AF_INET6,
-							  &((struct sockaddr_in6 *) addr)->sin6_addr.s6_addr,
-							  128,
-							  host_addr, sizeof(host_addr)) == NULL)
-				strcpy(host_addr, "???");
-		}
-#endif
-		else
-			strcpy(host_addr, "???");
+		getHostaddr(conn, host_addr, NI_MAXHOST);
 
 		/* To which host and port were we actually connecting? */
 		if (conn->connhost[conn->whichhost].type == CHT_HOST_ADDRESS)
@@ -2286,6 +2298,7 @@ keep_going:						/* We will come back to here until there is
 				 */
 				{
 					struct addrinfo *addr_cur = conn->addr_cur;
+					char		host_addr[NI_MAXHOST];
 
 					/*
 					 * Advance to next possible host, if we've tried all of
@@ -2302,6 +2315,17 @@ keep_going:						/* We will come back to here until there is
 						   addr_cur->ai_addrlen);
 					conn->raddr.salen = addr_cur->ai_addrlen;
 
+					/* set connip */
+					if (conn->connip != NULL)
+					{
+						free(conn->connip);
+						conn->connip = NULL;
+					}
+
+					getHostaddr(conn, host_addr, NI_MAXHOST);
+					if (strcmp(host_addr, UNKNOWN_HOST) != 0)
+						conn->connip = strdup(host_addr);
+
 					conn->sock = socket(addr_cur->ai_family, SOCK_STREAM, 0);
 					if (conn->sock == PGINVALID_SOCKET)
 					{
@@ -6172,6 +6196,25 @@ PQhost(const PGconn *conn)
 	return "";
 }
 
+char *
+PQhostaddr(const PGconn *conn)
+{
+	if (!conn)
+		return NULL;
+
+	if (conn->connhost != NULL)
+	{
+		if (conn->connhost[conn->whichhost].hostaddr != NULL &&
+			conn->connhost[conn->whichhost].hostaddr[0] != '\0')
+			return conn->connhost[conn->whichhost].hostaddr;
+
+		if (conn->connip != NULL)
+			return conn->connip;
+	}
+
+	return "";
+}
+
 char *
 PQport(const PGconn *conn)
 {
diff --git a/src/interfaces/libpq/libpq-fe.h b/src/interfaces/libpq/libpq-fe.h
index 52bd5d2cd8..3f13ddf092 100644
--- a/src/interfaces/libpq/libpq-fe.h
+++ b/src/interfaces/libpq/libpq-fe.h
@@ -312,6 +312,7 @@ extern char *PQdb(const PGconn *conn);
 extern char *PQuser(const PGconn *conn);
 extern char *PQpass(const PGconn *conn);
 extern char *PQhost(const PGconn *conn);
+extern char *PQhostaddr(const PGconn *conn);
 extern char *PQport(const PGconn *conn);
 extern char *PQtty(const PGconn *conn);
 extern char *PQoptions(const PGconn *conn);
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 975ab33d02..66fd317b94 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -397,6 +397,7 @@ struct pg_conn
 	int			nconnhost;		/* # of hosts named in conn string */
 	int			whichhost;		/* host we're currently trying/connected to */
 	pg_conn_host *connhost;		/* details about each named host */
+	char	   *connip;			/* IP address for current network connection */
 
 	/* Connection data */
 	pgsocket	sock;			/* FD for socket, PGINVALID_SOCKET if
#21Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Fabien COELHO (#20)
1 attachment(s)
Re: fix psql \conninfo & \connect when using hostaddr

On 2018-Nov-17, Fabien COELHO wrote:

Here is the updated v2

- libpq internal function getHostaddr get a length,
and I added an assert about it.
- added a few braces on if/if/else/if/else/else
- added an UNKNOWN_HOST macro to hide "???"
- moved host_addr[] declaration earlier to avoid some braces

You forgot to free(conn->connip) during freePGconn().

I found the UNKNOWN_HOST business quite dubious -- not only in your
patch but also in the existing coding. I changed the getHostname API so
that instead of returning "???" it sets the output buffer to the empty
string. AFAICS the only user-visible behavior is that we no longer
display the "???" in a parenthical comment next to the server address
when a connection fails (this is pre-existing behavior, not changed by
your patch.)

Now, maybe the thinking was that upon seeing this message:

could not connect to server: some error here
Is the server running on host "pg.mines-paristech.fr" (???) and accepting
connections on port 5432?

the user was going to think "oh, my machine must have run out of memory,
I'll reboot and retry". However, I highly doubt that anybody would
reach that conclusion without reading the source code. So I deem this
useless.

--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

Attachments:

0001-psql-Show-IP-address-in-conninfo-if-different-from-h.patchtext/x-diff; charset=us-asciiDownload
From 342559f657f0c2d7cdf20c29536652a9ceca1d2a Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: Mon, 19 Nov 2018 12:23:15 -0300
Subject: [PATCH] psql: Show IP address in \conninfo, if different from host

When hostaddr is given, the actual IP address that psql is connected to
can be totally unexpected for the given host.  The more verbose output
we now generate makes things clearer.  Since the "host" and "hostaddr"
parts of the conninfo could come from wildly different sources (say one
is in the service specification or a URI-style conninfo and the other is
not), this is useful.  This is also useful if a hostname resolves to
multiple addresses.

Author: Fabien Coelho
Reviewed-by: Pavel Stehule, Arthur Zakirov
Discussion: https://postgr.es/m/alpine.DEB.2.21.1810261532380.27686@lancre
	https://postgr.es/m/alpine.DEB.2.21.1808201323020.13832@lancre
---
 doc/src/sgml/libpq.sgml           | 30 ++++++++++++
 src/bin/psql/command.c            | 82 +++++++++++++++++++++++++-------
 src/interfaces/libpq/exports.txt  |  1 +
 src/interfaces/libpq/fe-connect.c | 98 +++++++++++++++++++++++++++++----------
 src/interfaces/libpq/libpq-fe.h   |  1 +
 src/interfaces/libpq/libpq-int.h  |  1 +
 6 files changed, 172 insertions(+), 41 deletions(-)

diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index 601091c570..d2e5b08541 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -1735,6 +1735,36 @@ char *PQhost(const PGconn *conn);
      </listitem>
     </varlistentry>
 
+
+    <varlistentry id="libpq-pqhostaddr">
+     <term>
+      <function>PQhostaddr</function>
+      <indexterm>
+       <primary>PQhostaddr</primary>
+      </indexterm>
+     </term>
+
+     <listitem>
+      <para>
+       Returns the server IP address of the active connection.
+       This can be the address that a host name resolved to,
+       or an IP address provided through the <literal>hostaddr</literal>
+       parameter.
+<synopsis>
+char *PQhostaddr(const PGconn *conn);
+</synopsis>
+      </para>
+
+      <para>
+       <function>PQhostaddr</function> returns <symbol>NULL</symbol> if the
+       <parameter>conn</parameter> argument is <symbol>NULL</symbol>.
+       Otherwise, if there is an error producing the host information
+       (perhaps if the connection has not been fully established or
+       there was an error), it returns an empty string.
+      </para>
+     </listitem>
+    </varlistentry>
+
     <varlistentry id="libpq-pqport">
      <term>
       <function>PQport</function>
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 04e227b5a6..ee88e1ca5c 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -596,14 +596,30 @@ exec_command_conninfo(PsqlScanState scan_state, bool active_branch)
 		else
 		{
 			char	   *host = PQhost(pset.db);
+			char	   *hostaddr = PQhostaddr(pset.db);
 
-			/* If the host is an absolute path, the connection is via socket */
+			/*
+			 * If the host is an absolute path, the connection is via socket
+			 * unless overriden by hostaddr
+			 */
 			if (is_absolute_path(host))
-				printf(_("You are connected to database \"%s\" as user \"%s\" via socket in \"%s\" at port \"%s\".\n"),
-					   db, PQuser(pset.db), host, PQport(pset.db));
+			{
+				if (hostaddr && *hostaddr)
+					printf(_("You are connected to database \"%s\" as user \"%s\" on address \"%s\" at port \"%s\".\n"),
+						   db, PQuser(pset.db), hostaddr, PQport(pset.db));
+				else
+					printf(_("You are connected to database \"%s\" as user \"%s\" via socket in \"%s\" at port \"%s\".\n"),
+						   db, PQuser(pset.db), host, PQport(pset.db));
+			}
 			else
-				printf(_("You are connected to database \"%s\" as user \"%s\" on host \"%s\" at port \"%s\".\n"),
-					   db, PQuser(pset.db), host, PQport(pset.db));
+			{
+				if (hostaddr && *hostaddr && strcmp(host, hostaddr) != 0)
+					printf(_("You are connected to database \"%s\" as user \"%s\" on host \"%s\" (address \"%s\") at port \"%s\".\n"),
+						   db, PQuser(pset.db), host, hostaddr, PQport(pset.db));
+				else
+					printf(_("You are connected to database \"%s\" as user \"%s\" on host \"%s\" at port \"%s\".\n"),
+						   db, PQuser(pset.db), host, PQport(pset.db));
+			}
 			printSSLInfo();
 		}
 	}
@@ -2854,6 +2870,7 @@ do_connect(enum trivalue reuse_previous_specification,
 	PGconn	   *o_conn = pset.db,
 			   *n_conn;
 	char	   *password = NULL;
+	char	   *hostaddr = NULL;
 	bool		keep_password;
 	bool		has_connection_string;
 	bool		reuse_previous;
@@ -2894,12 +2911,27 @@ do_connect(enum trivalue reuse_previous_specification,
 	}
 
 	/* grab missing values from the old connection */
-	if (!user && reuse_previous)
-		user = PQuser(o_conn);
-	if (!host && reuse_previous)
-		host = PQhost(o_conn);
-	if (!port && reuse_previous)
-		port = PQport(o_conn);
+	if (reuse_previous)
+	{
+		if (!user)
+			user = PQuser(o_conn);
+		if (host && strcmp(host, PQhost(o_conn)) == 0)
+		{
+			/*
+			 * if we are targetting the same host, reuse its hostaddr for
+			 * consistency
+			 */
+			hostaddr = PQhostaddr(o_conn);
+		}
+		if (!host)
+		{
+			host = PQhost(o_conn);
+			/* also set hostaddr for consistency */
+			hostaddr = PQhostaddr(o_conn);
+		}
+		if (!port)
+			port = PQport(o_conn);
+	}
 
 	/*
 	 * Any change in the parameters read above makes us discard the password.
@@ -2961,13 +2993,18 @@ do_connect(enum trivalue reuse_previous_specification,
 
 	while (true)
 	{
-#define PARAMS_ARRAY_SIZE	8
+#define PARAMS_ARRAY_SIZE	9
 		const char **keywords = pg_malloc(PARAMS_ARRAY_SIZE * sizeof(*keywords));
 		const char **values = pg_malloc(PARAMS_ARRAY_SIZE * sizeof(*values));
 		int			paramnum = -1;
 
 		keywords[++paramnum] = "host";
 		values[paramnum] = host;
+		if (hostaddr && *hostaddr)
+		{
+			keywords[++paramnum] = "hostaddr";
+			values[paramnum] = hostaddr;
+		}
 		keywords[++paramnum] = "port";
 		values[paramnum] = port;
 		keywords[++paramnum] = "user";
@@ -3071,14 +3108,27 @@ do_connect(enum trivalue reuse_previous_specification,
 			param_is_newly_set(PQport(o_conn), PQport(pset.db)))
 		{
 			char	   *host = PQhost(pset.db);
+			char	   *hostaddr = PQhostaddr(pset.db);
 
 			/* If the host is an absolute path, the connection is via socket */
 			if (is_absolute_path(host))
-				printf(_("You are now connected to database \"%s\" as user \"%s\" via socket in \"%s\" at port \"%s\".\n"),
-					   PQdb(pset.db), PQuser(pset.db), host, PQport(pset.db));
+			{
+				if (hostaddr && *hostaddr)
+					printf(_("You are now connected to database \"%s\" as user \"%s\" on address \"%s\" at port \"%s\".\n"),
+						   PQdb(pset.db), PQuser(pset.db), hostaddr, PQport(pset.db));
+				else
+					printf(_("You are now connected to database \"%s\" as user \"%s\" via socket in \"%s\" at port \"%s\".\n"),
+						   PQdb(pset.db), PQuser(pset.db), host, PQport(pset.db));
+			}
 			else
-				printf(_("You are now connected to database \"%s\" as user \"%s\" on host \"%s\" at port \"%s\".\n"),
-					   PQdb(pset.db), PQuser(pset.db), host, PQport(pset.db));
+			{
+				if (hostaddr && *hostaddr && strcmp(host, hostaddr) != 0)
+					printf(_("You are now connected to database \"%s\" as user \"%s\" on host \"%s\" (address \"%s\") at port \"%s\".\n"),
+						   PQdb(pset.db), PQuser(pset.db), host, hostaddr, PQport(pset.db));
+				else
+					printf(_("You are now connected to database \"%s\" as user \"%s\" on host \"%s\" at port \"%s\".\n"),
+						   PQdb(pset.db), PQuser(pset.db), host, PQport(pset.db));
+			}
 		}
 		else
 			printf(_("You are now connected to database \"%s\" as user \"%s\".\n"),
diff --git a/src/interfaces/libpq/exports.txt b/src/interfaces/libpq/exports.txt
index 4359fae30d..cc9ee9ce6b 100644
--- a/src/interfaces/libpq/exports.txt
+++ b/src/interfaces/libpq/exports.txt
@@ -173,3 +173,4 @@ PQsetErrorContextVisibility 170
 PQresultVerboseErrorMessage 171
 PQencryptPasswordConn     172
 PQresultMemorySize        173
+PQhostaddr                174
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index d001bc513d..bc456fec0c 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -1471,6 +1471,39 @@ connectNoDelay(PGconn *conn)
 	return 1;
 }
 
+/* ----------
+ * Write currently connected IP address into host_addr (of len host_addr_len).
+ * If unable to, set it to the empty string.
+ * ----------
+ */
+static void
+getHostaddr(PGconn *conn, char *host_addr, int host_addr_len)
+{
+	struct sockaddr_storage *addr = &conn->raddr.addr;
+
+	if (conn->connhost[conn->whichhost].type == CHT_HOST_ADDRESS)
+		strlcpy(host_addr, conn->connhost[conn->whichhost].hostaddr, host_addr_len);
+	else if (addr->ss_family == AF_INET)
+	{
+		if (inet_net_ntop(AF_INET,
+						  &((struct sockaddr_in *) addr)->sin_addr.s_addr,
+						  32,
+						  host_addr, host_addr_len) == NULL)
+			host_addr[0] = '\0';
+	}
+#ifdef HAVE_IPV6
+	else if (addr->ss_family == AF_INET6)
+	{
+		if (inet_net_ntop(AF_INET6,
+						  &((struct sockaddr_in6 *) addr)->sin6_addr.s6_addr,
+						  128,
+						  host_addr, host_addr_len) == NULL)
+			host_addr[0] = '\0';
+	}
+#endif
+	else
+		host_addr[0] = '\0';
+}
 
 /* ----------
  * connectFailureMessage -
@@ -1504,34 +1537,12 @@ connectFailureMessage(PGconn *conn, int errorno)
 		char		host_addr[NI_MAXHOST];
 		const char *displayed_host;
 		const char *displayed_port;
-		struct sockaddr_storage *addr = &conn->raddr.addr;
 
 		/*
 		 * Optionally display the network address with the hostname. This is
 		 * useful to distinguish between IPv4 and IPv6 connections.
 		 */
-		if (conn->connhost[conn->whichhost].type == CHT_HOST_ADDRESS)
-			strlcpy(host_addr, conn->connhost[conn->whichhost].hostaddr, NI_MAXHOST);
-		else if (addr->ss_family == AF_INET)
-		{
-			if (inet_net_ntop(AF_INET,
-							  &((struct sockaddr_in *) addr)->sin_addr.s_addr,
-							  32,
-							  host_addr, sizeof(host_addr)) == NULL)
-				strcpy(host_addr, "???");
-		}
-#ifdef HAVE_IPV6
-		else if (addr->ss_family == AF_INET6)
-		{
-			if (inet_net_ntop(AF_INET6,
-							  &((struct sockaddr_in6 *) addr)->sin6_addr.s6_addr,
-							  128,
-							  host_addr, sizeof(host_addr)) == NULL)
-				strcpy(host_addr, "???");
-		}
-#endif
-		else
-			strcpy(host_addr, "???");
+		getHostaddr(conn, host_addr, NI_MAXHOST);
 
 		/* To which host and port were we actually connecting? */
 		if (conn->connhost[conn->whichhost].type == CHT_HOST_ADDRESS)
@@ -1548,14 +1559,14 @@ connectFailureMessage(PGconn *conn, int errorno)
 		 * looked-up IP address.
 		 */
 		if (conn->connhost[conn->whichhost].type != CHT_HOST_ADDRESS &&
+			strlen(host_addr) > 0 &&
 			strcmp(displayed_host, host_addr) != 0)
 			appendPQExpBuffer(&conn->errorMessage,
 							  libpq_gettext("could not connect to server: %s\n"
 											"\tIs the server running on host \"%s\" (%s) and accepting\n"
 											"\tTCP/IP connections on port %s?\n"),
 							  SOCK_STRERROR(errorno, sebuf, sizeof(sebuf)),
-							  displayed_host,
-							  host_addr,
+							  displayed_host, host_addr,
 							  displayed_port);
 		else
 			appendPQExpBuffer(&conn->errorMessage,
@@ -2286,6 +2297,7 @@ keep_going:						/* We will come back to here until there is
 				 */
 				{
 					struct addrinfo *addr_cur = conn->addr_cur;
+					char		host_addr[NI_MAXHOST];
 
 					/*
 					 * Advance to next possible host, if we've tried all of
@@ -2302,6 +2314,21 @@ keep_going:						/* We will come back to here until there is
 						   addr_cur->ai_addrlen);
 					conn->raddr.salen = addr_cur->ai_addrlen;
 
+					/* set connip */
+					if (conn->connip != NULL)
+					{
+						free(conn->connip);
+						conn->connip = NULL;
+					}
+
+					getHostaddr(conn, host_addr, NI_MAXHOST);
+					if (strlen(host_addr) > 0)
+						conn->connip = strdup(host_addr);
+					/*
+					 * purposely ignore strdup failure; not a big problem if
+					 * it fails anyway.
+					 */
+
 					conn->sock = socket(addr_cur->ai_family, SOCK_STREAM, 0);
 					if (conn->sock == PGINVALID_SOCKET)
 					{
@@ -3665,6 +3692,8 @@ freePGconn(PGconn *conn)
 		free(conn->sslcompression);
 	if (conn->requirepeer)
 		free(conn->requirepeer);
+	if (conn->connip)
+		free(conn->connip);
 #if defined(ENABLE_GSS) || defined(ENABLE_SSPI)
 	if (conn->krbsrvname)
 		free(conn->krbsrvname);
@@ -6173,6 +6202,25 @@ PQhost(const PGconn *conn)
 }
 
 char *
+PQhostaddr(const PGconn *conn)
+{
+	if (!conn)
+		return NULL;
+
+	if (conn->connhost != NULL)
+	{
+		if (conn->connhost[conn->whichhost].hostaddr != NULL &&
+			conn->connhost[conn->whichhost].hostaddr[0] != '\0')
+			return conn->connhost[conn->whichhost].hostaddr;
+
+		if (conn->connip != NULL)
+			return conn->connip;
+	}
+
+	return "";
+}
+
+char *
 PQport(const PGconn *conn)
 {
 	if (!conn)
diff --git a/src/interfaces/libpq/libpq-fe.h b/src/interfaces/libpq/libpq-fe.h
index 52bd5d2cd8..3f13ddf092 100644
--- a/src/interfaces/libpq/libpq-fe.h
+++ b/src/interfaces/libpq/libpq-fe.h
@@ -312,6 +312,7 @@ extern char *PQdb(const PGconn *conn);
 extern char *PQuser(const PGconn *conn);
 extern char *PQpass(const PGconn *conn);
 extern char *PQhost(const PGconn *conn);
+extern char *PQhostaddr(const PGconn *conn);
 extern char *PQport(const PGconn *conn);
 extern char *PQtty(const PGconn *conn);
 extern char *PQoptions(const PGconn *conn);
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 975ab33d02..66fd317b94 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -397,6 +397,7 @@ struct pg_conn
 	int			nconnhost;		/* # of hosts named in conn string */
 	int			whichhost;		/* host we're currently trying/connected to */
 	pg_conn_host *connhost;		/* details about each named host */
+	char	   *connip;			/* IP address for current network connection */
 
 	/* Connection data */
 	pgsocket	sock;			/* FD for socket, PGINVALID_SOCKET if
-- 
2.11.0

#22Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Alvaro Herrera (#21)
Re: fix psql \conninfo & \connect when using hostaddr

Pushed, thanks.

--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#23Fabien COELHO
coelho@cri.ensmp.fr
In reply to: Alvaro Herrera (#21)
Re: fix psql \conninfo & \connect when using hostaddr

Hello Alvaro,

- libpq internal function getHostaddr get a length,
and I added an assert about it.
- added a few braces on if/if/else/if/else/else
- added an UNKNOWN_HOST macro to hide "???"
- moved host_addr[] declaration earlier to avoid some braces

You forgot to free(conn->connip) during freePGconn().

Argh, indeed:-(

I found the UNKNOWN_HOST business quite dubious -- not only in your
patch but also in the existing coding.

So did I:-) I only kept it because it was already done like that.

I changed the getHostname API so that instead of returning "???" it sets
the output buffer to the empty string.

I hesitated to do exactly that. I'm fine with that.

Thanks for the push.

--
Fabien.

#24Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Michael Paquier (#11)
Re: fix psql \conninfo & \connect when using hostaddr

On 2018-Nov-09, Michael Paquier wrote:

On Thu, Nov 08, 2018 at 12:13:31PM -0300, Alvaro Herrera wrote:

Right, that too. Fortunately I think compilers warn about
mismatching indentation nowadays, at least in some cases.

I don't recall seeing a compiler warning about that, but I do recall
coverity complaining loudly about such things.

:-)

/pgsql/source/master/src/backend/catalog/namespace.c: In function 'InitRemoveTempRelations':
/pgsql/source/master/src/backend/catalog/namespace.c:4132:2: warning: this 'if' clause does not guard... [-Wmisleading-indentation]
if (OidIsValid(namespaceOid))
^~
/pgsql/source/master/src/backend/catalog/namespace.c:4134:3: note: ...this statement, but the latter is misleadingly indented as if it is guarded by the 'if'
get_namespace_oid(namespaceName, true);
^~~~~~~~~~~~~~~~~

$ gcc --version
gcc (Debian 6.3.0-18+deb9u1) 6.3.0 20170516

--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#25Noah Misch
noah@leadboat.com
In reply to: Alvaro Herrera (#21)
Re: fix psql \conninfo & \connect when using hostaddr

On Mon, Nov 19, 2018 at 12:53:15PM -0300, Alvaro Herrera wrote:

commit 6e5f8d4
Commit: Alvaro Herrera <alvherre@alvh.no-ip.org>
CommitDate: Mon Nov 19 14:34:12 2018 -0300

psql: Show IP address in \conninfo

Discussion: /messages/by-id/alpine.DEB.2.21.1810261532380.27686@lancre
/messages/by-id/alpine.DEB.2.21.1808201323020.13832@lancre

--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -2894,12 +2911,27 @@ do_connect(enum trivalue reuse_previous_specification,
}
/* grab missing values from the old connection */
-	if (!user && reuse_previous)
-		user = PQuser(o_conn);
-	if (!host && reuse_previous)
-		host = PQhost(o_conn);
-	if (!port && reuse_previous)
-		port = PQport(o_conn);
+	if (reuse_previous)
+	{
+		if (!user)
+			user = PQuser(o_conn);
+		if (host && strcmp(host, PQhost(o_conn)) == 0)
+		{
+			/*
+			 * if we are targetting the same host, reuse its hostaddr for
+			 * consistency
+			 */
+			hostaddr = PQhostaddr(o_conn);
+		}
+		if (!host)
+		{
+			host = PQhost(o_conn);
+			/* also set hostaddr for consistency */
+			hostaddr = PQhostaddr(o_conn);
+		}
+		if (!port)
+			port = PQport(o_conn);
+	}

/*
* Any change in the parameters read above makes us discard the password.

The "hostaddr = PQhostaddr(o_conn)" branches make \connect use the same IP
address as the existing connection. I like that when the existing connection
uses a hostaddr= parameter, but I doubt it's the right thing otherwise. If
the server IP changes, \connect should find the server at its new IP. If the
server has multiple IPs, \connect should have the opportunity to try them all,
just like the original connection attempt could have.

Other than that, the \connect behavior change makes sense to me. However,
nothing updated \connect documentation. (Even the commit log message said
nothing about \connect.)

@@ -3071,14 +3108,27 @@ do_connect(enum trivalue reuse_previous_specification,

/* If the host is an absolute path, the connection is via socket */

This comment is no longer correct. Copy the other "via socket" comment.

--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -1471,6 +1471,39 @@ connectNoDelay(PGconn *conn)
return 1;
}
+/* ----------
+ * Write currently connected IP address into host_addr (of len host_addr_len).
+ * If unable to, set it to the empty string.
+ * ----------
+ */
+static void
+getHostaddr(PGconn *conn, char *host_addr, int host_addr_len)
+{
+	struct sockaddr_storage *addr = &conn->raddr.addr;
+
+	if (conn->connhost[conn->whichhost].type == CHT_HOST_ADDRESS)
+		strlcpy(host_addr, conn->connhost[conn->whichhost].hostaddr, host_addr_len);

I recommend removing this branch; inet_net_ntop() would work fine in the
CHT_HOST_ADDRESS case, and that has the advantage of putting the address in
standard form. Currently, hostaddr in nonstandard form stays that way
(trailing space, in this example):

[nm@gust 8:1 2019-05-23T13:21:54 postgresql 0]$ psql -X "hostaddr='127.0.7.1 '" -c '\conninfo'
You are connected to database "test" as user "nm" on host "127.0.7.1 " at port "5433".

@@ -6173,6 +6202,25 @@ PQhost(const PGconn *conn)
}

char *
+PQhostaddr(const PGconn *conn)
+{
+	if (!conn)
+		return NULL;
+
+	if (conn->connhost != NULL)
+	{
+		if (conn->connhost[conn->whichhost].hostaddr != NULL &&
+			conn->connhost[conn->whichhost].hostaddr[0] != '\0')
+			return conn->connhost[conn->whichhost].hostaddr;
+
+		if (conn->connip != NULL)
+			return conn->connip;
+	}
+
+	return "";
+}

Similar to my comment on getHostaddr(), why ever use hostaddr? connip should
always be equivalent to hostaddr when hostaddr is set. (connip may be NULL if
a malloc failed, but if we really cared, we'd fail the connection attempt when
that happens. If connip differs in any other material way, I'd want the user
to see connip.)

#26Dmitry Dolgov
9erthalion6@gmail.com
In reply to: Noah Misch (#25)
Re: fix psql \conninfo & \connect when using hostaddr

On Mon, May 27, 2019 at 10:38 PM Noah Misch <noah@leadboat.com> wrote:

On Mon, Nov 19, 2018 at 12:53:15PM -0300, Alvaro Herrera wrote:

commit 6e5f8d4
Commit: Alvaro Herrera <alvherre@alvh.no-ip.org>
CommitDate: Mon Nov 19 14:34:12 2018 -0300

psql: Show IP address in \conninfo

Discussion: /messages/by-id/alpine.DEB.2.21.1810261532380.27686@lancre
/messages/by-id/alpine.DEB.2.21.1808201323020.13832@lancre

--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -2894,12 +2911,27 @@ do_connect(enum trivalue reuse_previous_specification,
}
/* grab missing values from the old connection */
-     if (!user && reuse_previous)
-             user = PQuser(o_conn);
-     if (!host && reuse_previous)
-             host = PQhost(o_conn);
-     if (!port && reuse_previous)
-             port = PQport(o_conn);
+     if (reuse_previous)
+     {
+             if (!user)
+                     user = PQuser(o_conn);
+             if (host && strcmp(host, PQhost(o_conn)) == 0)
+             {
+                     /*
+                      * if we are targetting the same host, reuse its hostaddr for
+                      * consistency
+                      */
+                     hostaddr = PQhostaddr(o_conn);
+             }
+             if (!host)
+             {
+                     host = PQhost(o_conn);
+                     /* also set hostaddr for consistency */
+                     hostaddr = PQhostaddr(o_conn);
+             }
+             if (!port)
+                     port = PQport(o_conn);
+     }

/*
* Any change in the parameters read above makes us discard the password.

The "hostaddr = PQhostaddr(o_conn)" branches make \connect use the same IP
address as the existing connection. I like that when the existing connection
uses a hostaddr= parameter, but I doubt it's the right thing otherwise. If
the server IP changes, \connect should find the server at its new IP. If the
server has multiple IPs, \connect should have the opportunity to try them all,
just like the original connection attempt could have.

Other than that, the \connect behavior change makes sense to me. However,
nothing updated \connect documentation. (Even the commit log message said
nothing about \connect.)

Given that it's an open item for PostgreSQL 12, I've decided to take a look.
Indeed, looks like 6e5f8d4 introduced a subtle behaviour change, when hostaddr
changes are not picked up for subsequent \connect's, and I don't see any
mentions of it in the documentation. Although I guess it can be avoided by
`-reuse-previous=off`, probably it makese sense to update the docs.

#27Fabien COELHO
coelho@cri.ensmp.fr
In reply to: Dmitry Dolgov (#26)
Re: fix psql \conninfo & \connect when using hostaddr

Hello Dmitry,

Given that it's an open item for PostgreSQL 12,

I'm working on it, but slowly.

I've decided to take a look.

Thanks!

Indeed, looks like 6e5f8d4 introduced a subtle behaviour change, when
hostaddr changes are not picked up for subsequent \connect's, and I
don't see any mentions of it in the documentation.

ISTM that the documentation is kind of fuzzy enough to match both
behaviors.

Although I guess it can be avoided by `-reuse-previous=off`, probably it
makese sense to update the docs.

Yep, that is one option. The other is to revert or alter the subtle
change, but ISTM that it made sense in some use case, so I wanted some
time to think about it and test.

--
Fabien.

#28Dmitry Dolgov
9erthalion6@gmail.com
In reply to: Fabien COELHO (#27)
Re: fix psql \conninfo & \connect when using hostaddr

On Tue, Jun 11, 2019 at 6:46 AM Fabien COELHO <coelho@cri.ensmp.fr> wrote:

Given that it's an open item for PostgreSQL 12,

I'm working on it, but slowly.

Sorry, didn't mean to hurry you :) In fact if you need a hand, I can prepare a
patch for those parts everyone can agree on.

Indeed, looks like 6e5f8d4 introduced a subtle behaviour change, when
hostaddr changes are not picked up for subsequent \connect's, and I
don't see any mentions of it in the documentation.

ISTM that the documentation is kind of fuzzy enough to match both
behaviors.

Yeah, right. Then maybe we can add `hostaddr` e.g. somewhere here:

    diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
    --- a/doc/src/sgml/ref/psql-ref.sgml
    +++ b/doc/src/sgml/ref/psql-ref.sgml
    @@ -898,7 +898,7 @@ testdb=&gt;
             </para>
             <para>
    -        Where the command omits database name, user, host, or port, the new
    +        Where the command omits database name, user, host,
hostaddr, or port, the new
             connection can reuse values from the previous connection.
By default,
             values from the previous connection are reused except
when processing

to emphasize the reusing of hostaddr too, and then mention behaviour change in
the release notes or any other place that would be the appropriate for that?

Although I guess it can be avoided by `-reuse-previous=off`, probably it
makese sense to update the docs.

Yep, that is one option. The other is to revert or alter the subtle
change, but ISTM that it made sense in some use case, so I wanted some
time to think about it and test.

Sure, no one argue that the behaviour should be changed, it's only about the
documentation part.

On Mon, May 27, 2019 at 10:38 PM Noah Misch <noah@leadboat.com> wrote:
+static void
+getHostaddr(PGconn *conn, char *host_addr, int host_addr_len)
+{
+     struct sockaddr_storage *addr = &conn->raddr.addr;
+
+     if (conn->connhost[conn->whichhost].type == CHT_HOST_ADDRESS)
+             strlcpy(host_addr, conn->connhost[conn->whichhost].hostaddr, host_addr_len);

I recommend removing this branch; inet_net_ntop() would work fine in the
CHT_HOST_ADDRESS case, and that has the advantage of putting the address in
standard form.

After short experiments I also couldn't find the reason why CHT_HOST_ADDRESS is
treated specially.

+PQhostaddr(const PGconn *conn)
+{
+     if (!conn)
+             return NULL;
+
+     if (conn->connhost != NULL)
+     {
+             if (conn->connhost[conn->whichhost].hostaddr != NULL &&
+                     conn->connhost[conn->whichhost].hostaddr[0] != '\0')
+                     return conn->connhost[conn->whichhost].hostaddr;
+
+             if (conn->connip != NULL)
+                     return conn->connip;
+     }
+
+     return "";
+}

Similar to my comment on getHostaddr(), why ever use hostaddr? connip should
always be equivalent to hostaddr when hostaddr is set. (connip may be NULL if
a malloc failed, but if we really cared, we'd fail the connection attempt when
that happens. If connip differs in any other material way, I'd want the user
to see connip.)

The same here. Couldn't find any situation so far when `hostaddr` would be
different from `connip`. Maybe it makes sense just to reverse these conditions
and return first `connip` if not NULL. Also this example:

Currently, hostaddr in nonstandard form stays that way
(trailing space, in this example):

[nm@gust 8:1 2019-05-23T13:21:54 postgresql 0]$ psql -X "hostaddr='127.0.7.1 '" -c '\conninfo'
You are connected to database "test" as user "nm" on host "127.0.7.1 " at port "5433".

was a bit confusing for me, since the value here comes from PQhost, not
PQhostaddr, but in the very same way via hostaddr. So I guess any changes
should be replicated there too.

#29Noah Misch
noah@leadboat.com
In reply to: Dmitry Dolgov (#28)
Re: fix psql \conninfo & \connect when using hostaddr

On Tue, Jun 11, 2019 at 01:59:20PM +0200, Dmitry Dolgov wrote:

On Tue, Jun 11, 2019 at 6:46 AM Fabien COELHO <coelho@cri.ensmp.fr> wrote:

Although I guess it can be avoided by `-reuse-previous=off`, probably it
makese sense to update the docs.

Yep, that is one option. The other is to revert or alter the subtle
change, but ISTM that it made sense in some use case, so I wanted some
time to think about it and test.

Sure, no one argue that the behaviour should be changed, it's only about the
documentation part.

No, I was arguing that a behavior should revert back its v11 behavior:

\connect mydb myuser myhost
-- should resolve "myhost" again, like it did in v11
\connect

\connect "dbname=mydb host=myhost hostaddr=127.0.0.1"
-- ok to reuse hostaddr=127.0.0.1; I agree that's a feature
\connect

#30Dmitry Dolgov
9erthalion6@gmail.com
In reply to: Noah Misch (#29)
Re: fix psql \conninfo & \connect when using hostaddr

On Wed, Jun 12, 2019 at 9:45 AM Noah Misch <noah@leadboat.com> wrote:

On Tue, Jun 11, 2019 at 01:59:20PM +0200, Dmitry Dolgov wrote:

On Tue, Jun 11, 2019 at 6:46 AM Fabien COELHO <coelho@cri.ensmp.fr> wrote:

Although I guess it can be avoided by `-reuse-previous=off`, probably it
makese sense to update the docs.

Yep, that is one option. The other is to revert or alter the subtle
change, but ISTM that it made sense in some use case, so I wanted some
time to think about it and test.

Sure, no one argue that the behaviour should be changed, it's only about the
documentation part.

No, I was arguing that a behavior should revert back its v11 behavior:

\connect mydb myuser myhost
-- should resolve "myhost" again, like it did in v11
\connect

\connect "dbname=mydb host=myhost hostaddr=127.0.0.1"
-- ok to reuse hostaddr=127.0.0.1; I agree that's a feature
\connect

Oh, ok, sorry for misunderstanding.

#31Fabien COELHO
coelho@cri.ensmp.fr
In reply to: Noah Misch (#29)
Re: fix psql \conninfo & \connect when using hostaddr

Hello Noah,

Although I guess it can be avoided by `-reuse-previous=off`, probably it
makese sense to update the docs.

Yep, that is one option. The other is to revert or alter the subtle
change, but ISTM that it made sense in some use case, so I wanted some
time to think about it and test.

Sure, no one argue that the behaviour should be changed, it's only about the
documentation part.

No, I was arguing that a behavior should revert back its v11 behavior:

I got that. I'm working on it, and on the other issues you raised.

The issue I see is what do we want when a name resolves to multiple
addresses. The answer is not fully obvious to me right now. I'll try to
send a patch over the week-end.

\connect mydb myuser myhost
-- should resolve "myhost" again, like it did in v11
\connect

\connect "dbname=mydb host=myhost hostaddr=127.0.0.1"
-- ok to reuse hostaddr=127.0.0.1; I agree that's a feature
\connect

--
Fabien.

#32Fabien COELHO
coelho@cri.ensmp.fr
In reply to: Fabien COELHO (#31)
1 attachment(s)
Re: fix psql \conninfo & \connect when using hostaddr

Hello,

I got that. I'm working on it, and on the other issues you raised.

The issue I see is what do we want when a name resolves to multiple
addresses. The answer is not fully obvious to me right now. I'll try to send
a patch over the week-end.

At Alvaro's request, here is a quick WIP patch, that does not do the right
thing, because there is no simple way to know whether hostaddr was set at
the libPQ level, so either we set it always, about which Noah complained,
or we don't, about which someone else will complain quite easily, i.e.
with this patch

\c "host=foo hostaddr=ip"

connects to ip, but then

\c

will reconnect to foo but ignore ip. Well, ISTM that this is back to the
previous doubtful behavior, so at least it is not a regression, just the
same bug:-)

A solution could be to have a PQdoestheconnectionuseshostaddr(conn)
function, but I cannot say I'd be thrilled.

Another option would be to import PGconn full definition in
"psql/command.c", but that would break the PQ interface, I cannot say I'd
be thrilled either.

The patch returns host as defined by the user, but the regenerated
hostaddr (aka connip), which is not an homogeneous behavior. PQhost should
probably use connip if host was set as an ip, but that needs guessing.

The underlying issue is that the host/hostaddr stuff is not that easy to
fix.

At least, after the patch the connection information (\conninfo) is still
the right one, which is an improvement.

--
Fabien.

Attachments:

connect-fix-1.patchtext/x-diff; name=connect-fix-1.patchDownload
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 695d6ba9f1..4bf4726981 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -2937,16 +2937,16 @@ do_connect(enum trivalue reuse_previous_specification,
 		if (host && strcmp(host, PQhost(o_conn)) == 0)
 		{
 			/*
-			 * if we are targeting the same host, reuse its hostaddr for
-			 * consistency
+			 * if we are targeting the same host, we should reuse its hostaddr for
+			 * consistency if hostaddr was explicitely set. However, the library
+			 * does not allow to know that at the libPQ level.
 			 */
-			hostaddr = PQhostaddr(o_conn);
+			;
 		}
 		if (!host)
 		{
 			host = PQhost(o_conn);
-			/* also set hostaddr for consistency */
-			hostaddr = PQhostaddr(o_conn);
+			/* we should also set hostaddr for consistency, if hostaddr was set */
 		}
 		if (!port)
 			port = PQport(o_conn);
@@ -3129,7 +3129,10 @@ do_connect(enum trivalue reuse_previous_specification,
 			char	   *host = PQhost(pset.db);
 			char	   *hostaddr = PQhostaddr(pset.db);
 
-			/* If the host is an absolute path, the connection is via socket */
+			/*
+			 * If the host is an absolute path, the connection is via socket
+			 * unless overriden by hostaddr
+			 */
 			if (is_absolute_path(host))
 			{
 				if (hostaddr && *hostaddr)
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index e58fa6742a..5d88c15cc5 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -1536,9 +1536,7 @@ getHostaddr(PGconn *conn, char *host_addr, int host_addr_len)
 {
 	struct sockaddr_storage *addr = &conn->raddr.addr;
 
-	if (conn->connhost[conn->whichhost].type == CHT_HOST_ADDRESS)
-		strlcpy(host_addr, conn->connhost[conn->whichhost].hostaddr, host_addr_len);
-	else if (addr->ss_family == AF_INET)
+	if (addr->ss_family == AF_INET)
 	{
 		if (inet_net_ntop(AF_INET,
 						  &((struct sockaddr_in *) addr)->sin_addr.s_addr,
@@ -6463,6 +6461,11 @@ PQhost(const PGconn *conn)
 
 	if (conn->connhost != NULL)
 	{
+		/*
+		 * note this return the host=... value provided by the user,
+		 * even if it is an IP, this it can include spaces and so,
+		 * whereas the next function uses the regenerated IP.
+		 */
 		if (conn->connhost[conn->whichhost].host != NULL &&
 			conn->connhost[conn->whichhost].host[0] != '\0')
 			return conn->connhost[conn->whichhost].host;
@@ -6480,15 +6483,8 @@ PQhostaddr(const PGconn *conn)
 	if (!conn)
 		return NULL;
 
-	if (conn->connhost != NULL)
-	{
-		if (conn->connhost[conn->whichhost].hostaddr != NULL &&
-			conn->connhost[conn->whichhost].hostaddr[0] != '\0')
-			return conn->connhost[conn->whichhost].hostaddr;
-
-		if (conn->connip != NULL)
-			return conn->connip;
-	}
+	if (conn->connhost != NULL && conn->connip != NULL)
+		return conn->connip;
 
 	return "";
 }
#33Noah Misch
noah@leadboat.com
In reply to: Fabien COELHO (#32)
Re: fix psql \conninfo & \connect when using hostaddr

On Wed, Jun 12, 2019 at 02:44:49PM +0200, Fabien COELHO wrote:

there is no simple way to know whether hostaddr was set at
the libPQ level

A solution could be to have a PQdoestheconnectionuseshostaddr(conn)
function, but I cannot say I'd be thrilled.

PQconninfo() is the official way to retrieve that.

#34Fabien COELHO
coelho@cri.ensmp.fr
In reply to: Noah Misch (#33)
1 attachment(s)
Re: fix psql \conninfo & \connect when using hostaddr

Hello Noah,

there is no simple way to know whether hostaddr was set at
the libPQ level

A solution could be to have a PQdoestheconnectionuseshostaddr(conn)
function, but I cannot say I'd be thrilled.

PQconninfo() is the official way to retrieve that.

Thanks for the pointer! I did not notice this one. At least the API looks
better than the one I was suggesting:-)

ISTM that this function could be used to set other parameters, fixing some
other issues such as ignoring special parameters on reconnections.

Anyway, attached an attempt at implementing the desired behavior wrt
hostaddr.

--
Fabien.

Attachments:

connect-fix-2.patchtext/x-diff; name=connect-fix-2.patchDownload
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 695d6ba9f1..205ba3f602 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -2870,6 +2870,23 @@ param_is_newly_set(const char *old_val, const char *new_val)
 	return false;
 }
 
+/* return whether hostaddr was used for the connection. */
+static bool
+has_hostaddr(PGconn *conn)
+{
+	bool				used = false;
+	PQconninfoOption   *ciopt = PQconninfo(conn);
+
+	for (PQconninfoOption *p = ciopt; p->keyword != NULL; p++)
+	{
+		if (strcmp(p->keyword, "hostaddr") == 0 && p->val != NULL)
+			used = true;
+	}
+
+	PQconninfoFree(ciopt);
+	return used;
+}
+
 /*
  * do_connect -- handler for \connect
  *
@@ -2937,16 +2954,18 @@ do_connect(enum trivalue reuse_previous_specification,
 		if (host && strcmp(host, PQhost(o_conn)) == 0)
 		{
 			/*
-			 * if we are targeting the same host, reuse its hostaddr for
-			 * consistency
+			 * if we are targeting the same host, we reuse its hostaddr for
+			 * consistency if hostaddr was explicitely set.
 			 */
-			hostaddr = PQhostaddr(o_conn);
+			if (has_hostaddr(o_conn))
+				hostaddr = PQhostaddr(o_conn);
 		}
 		if (!host)
 		{
 			host = PQhost(o_conn);
-			/* also set hostaddr for consistency */
-			hostaddr = PQhostaddr(o_conn);
+			/* we also set hostaddr for consistency, if hostaddr was set */
+			if (has_hostaddr(o_conn))
+				hostaddr = PQhostaddr(o_conn);
 		}
 		if (!port)
 			port = PQport(o_conn);
@@ -3129,7 +3148,10 @@ do_connect(enum trivalue reuse_previous_specification,
 			char	   *host = PQhost(pset.db);
 			char	   *hostaddr = PQhostaddr(pset.db);
 
-			/* If the host is an absolute path, the connection is via socket */
+			/*
+			 * If the host is an absolute path, the connection is via socket
+			 * unless overriden by hostaddr
+			 */
 			if (is_absolute_path(host))
 			{
 				if (hostaddr && *hostaddr)
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index e58fa6742a..325d86e05e 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -1536,9 +1536,7 @@ getHostaddr(PGconn *conn, char *host_addr, int host_addr_len)
 {
 	struct sockaddr_storage *addr = &conn->raddr.addr;
 
-	if (conn->connhost[conn->whichhost].type == CHT_HOST_ADDRESS)
-		strlcpy(host_addr, conn->connhost[conn->whichhost].hostaddr, host_addr_len);
-	else if (addr->ss_family == AF_INET)
+	if (addr->ss_family == AF_INET)
 	{
 		if (inet_net_ntop(AF_INET,
 						  &((struct sockaddr_in *) addr)->sin_addr.s_addr,
@@ -6463,6 +6461,11 @@ PQhost(const PGconn *conn)
 
 	if (conn->connhost != NULL)
 	{
+		/*
+		 * note this return the host/hosname=... value provided by the user,
+		 * even if it is an IP, thus it can include spaces and so,
+		 * whereas the next function uses the regenerated IP.
+		 */
 		if (conn->connhost[conn->whichhost].host != NULL &&
 			conn->connhost[conn->whichhost].host[0] != '\0')
 			return conn->connhost[conn->whichhost].host;
@@ -6480,15 +6483,12 @@ PQhostaddr(const PGconn *conn)
 	if (!conn)
 		return NULL;
 
-	if (conn->connhost != NULL)
-	{
-		if (conn->connhost[conn->whichhost].hostaddr != NULL &&
-			conn->connhost[conn->whichhost].hostaddr[0] != '\0')
-			return conn->connhost[conn->whichhost].hostaddr;
-
-		if (conn->connip != NULL)
-			return conn->connip;
-	}
+	/*
+	 * Always return the regenerated IP address string so that
+	 * the result is normalized.
+	 */
+	if (conn->connhost != NULL && conn->connip != NULL)
+		return conn->connip;
 
 	return "";
 }
#35Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Noah Misch (#25)
Re: fix psql \conninfo & \connect when using hostaddr

On 2019-May-27, Noah Misch wrote:

Other than that, the \connect behavior change makes sense to me. However,
nothing updated \connect documentation. (Even the commit log message said
nothing about \connect.)

I added this blurb to the paragraph that documents the reusing behavior:

If <literal>hostaddr</literal> was specified in the original
connection's <structname>conninfo</structname>, that address is reused
for the new connection (disregarding any other host specification).

Thanks for reporting these problems. I'm going to push this shortly.
We can revise over the weekend, if there's need.

--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#36Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Fabien COELHO (#34)
Re: fix psql \conninfo & \connect when using hostaddr

On 2019-Jun-13, Fabien COELHO wrote:

Thanks for the pointer! I did not notice this one. At least the API looks
better than the one I was suggesting:-)

ISTM that this function could be used to set other parameters, fixing some
other issues such as ignoring special parameters on reconnections.

Anyway, attached an attempt at implementing the desired behavior wrt
hostaddr.

BTW I tested this manually and it seemed to work as intended, ie., if I
change /etc/hosts for the hostname I am using between one connection and
the next, we do keep the hostaddr if it was specified, or we resolve the
name again if it's not.

--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#37Michael Paquier
michael@paquier.xyz
In reply to: Alvaro Herrera (#36)
Re: fix psql \conninfo & \connect when using hostaddr

On Fri, Jun 14, 2019 at 06:31:52PM -0400, Alvaro Herrera wrote:

BTW I tested this manually and it seemed to work as intended, ie., if I
change /etc/hosts for the hostname I am using between one connection and
the next, we do keep the hostaddr if it was specified, or we resolve the
name again if it's not.

Alvaro, did 313f56ce fix all the issues related to this thread?
Perhaps this open item can now be closed?
--
Michael

#38Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Michael Paquier (#37)
Re: fix psql \conninfo & \connect when using hostaddr

On 2019-Jun-24, Michael Paquier wrote:

On Fri, Jun 14, 2019 at 06:31:52PM -0400, Alvaro Herrera wrote:

BTW I tested this manually and it seemed to work as intended, ie., if I
change /etc/hosts for the hostname I am using between one connection and
the next, we do keep the hostaddr if it was specified, or we resolve the
name again if it's not.

Alvaro, did 313f56ce fix all the issues related to this thread?

Yes, as far as I am aware it does.

Perhaps this open item can now be closed?

I left it there so that others could double-check if there were still
some things needing tweaks. Feel free to close it now, thanks.

--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#39Noah Misch
noah@leadboat.com
In reply to: Alvaro Herrera (#38)
Re: fix psql \conninfo & \connect when using hostaddr

On Mon, Jun 24, 2019 at 08:52:00AM -0400, Alvaro Herrera wrote:

On 2019-Jun-24, Michael Paquier wrote:

On Fri, Jun 14, 2019 at 06:31:52PM -0400, Alvaro Herrera wrote:

BTW I tested this manually and it seemed to work as intended, ie., if I
change /etc/hosts for the hostname I am using between one connection and
the next, we do keep the hostaddr if it was specified, or we resolve the
name again if it's not.

Alvaro, did 313f56ce fix all the issues related to this thread?

Yes, as far as I am aware it does.

I agree, it did.

#40Michael Paquier
michael@paquier.xyz
In reply to: Noah Misch (#39)
Re: fix psql \conninfo & \connect when using hostaddr

On Mon, Jun 24, 2019 at 04:51:04PM -0700, Noah Misch wrote:

On Mon, Jun 24, 2019 at 08:52:00AM -0400, Alvaro Herrera wrote:

On 2019-Jun-24, Michael Paquier wrote:

Alvaro, did 313f56ce fix all the issues related to this thread?

Yes, as far as I am aware it does.

I agree, it did.

Indeed. I have been looking at the thread and I can see the
difference with and without the patch committed. This open item is
closed.
--
Michael