From f0fe716f18c586dd5a7085647211b73267068483 Mon Sep 17 00:00:00 2001
From: Jacob Champion <champion.p@gmail.com>
Date: Sun, 17 May 2026 20:05:43 -0700
Subject: [PATCH 2/3] libpq: Break address resolution into its own helper

In preparation for a new resolver implementation, split the logic that
performs resolution out into a new function.

TODO: probably switch to macro constants for the return value of
      resolve_next_host()
---
 src/interfaces/libpq/fe-connect.c | 258 ++++++++++++++++--------------
 1 file changed, 140 insertions(+), 118 deletions(-)

diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 38422becc48..39241002998 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -466,6 +466,7 @@ static PGPing internal_ping(PGconn *conn);
 static void pqFreeCommandQueue(PGcmdQueueEntry *queue);
 static bool fillPGconn(PGconn *conn, PQconninfoOption *connOptions);
 static void freePGconn(PGconn *conn);
+static int	resolve_next_host(PGconn *conn);
 static void release_conn_addrinfo(PGconn *conn);
 static int	store_conn_addrinfo(PGconn *conn, struct addrinfo *addrlist);
 static void sendTerminateConn(PGconn *conn);
@@ -3001,131 +3002,18 @@ keep_going:						/* We will come back to here until there is
 	/* Time to advance to next connhost[] entry? */
 	if (conn->try_next_host)
 	{
-		pg_conn_host *ch;
-		struct addrinfo hint;
-		struct addrinfo *addrlist;
-		int			thisport;
 		int			ret;
-		char		portstr[MAXPGPATH];
-
-		if (conn->whichhost + 1 < conn->nconnhost)
-			conn->whichhost++;
-		else
-		{
-			/*
-			 * Oops, no more hosts.
-			 *
-			 * If we are trying to connect in "prefer-standby" mode, then drop
-			 * the standby requirement and start over. Don't do this for
-			 * cancel requests though, since we are certain the list of
-			 * servers won't change as the target_server_type option is not
-			 * applicable to those connections.
-			 *
-			 * Otherwise, an appropriate error message is already set up, so
-			 * we just need to set the right status.
-			 */
-			if (conn->target_server_type == SERVER_TYPE_PREFER_STANDBY &&
-				conn->nconnhost > 0 &&
-				!conn->cancelRequest)
-			{
-				conn->target_server_type = SERVER_TYPE_PREFER_STANDBY_PASS2;
-				conn->whichhost = 0;
-			}
-			else
-				goto error_return;
-		}
-
-		/* Drop any address info for previous host */
-		release_conn_addrinfo(conn);
 
 		/*
 		 * Look up info for the new host.  On failure, log the problem in
 		 * conn->errorMessage, then loop around to try the next host.  (Note
 		 * we don't clear try_next_host until we've succeeded.)
 		 */
-		ch = &conn->connhost[conn->whichhost];
-
-		/* Initialize hint structure */
-		MemSet(&hint, 0, sizeof(hint));
-		hint.ai_socktype = SOCK_STREAM;
-		hint.ai_family = AF_UNSPEC;
-
-		/* Figure out the port number we're going to use. */
-		if (ch->port == NULL || ch->port[0] == '\0')
-			thisport = DEF_PGPORT;
-		else
-		{
-			if (!pqParseIntParam(ch->port, &thisport, conn, "port"))
-				goto error_return;
-
-			if (thisport < 1 || thisport > 65535)
-			{
-				libpq_append_conn_error(conn, "invalid port number: \"%s\"", ch->port);
-				goto keep_going;
-			}
-		}
-		snprintf(portstr, sizeof(portstr), "%d", thisport);
-
-		/* Use pg_getaddrinfo_all() to resolve the address */
-		switch (ch->type)
-		{
-			case CHT_HOST_NAME:
-				ret = pg_getaddrinfo_all(ch->host, portstr, &hint,
-										 &addrlist);
-				if (ret || !addrlist)
-				{
-					libpq_append_conn_error(conn, "could not translate host name \"%s\" to address: %s",
-											ch->host, gai_strerror(ret));
-					goto keep_going;
-				}
-				break;
-
-			case CHT_HOST_ADDRESS:
-				hint.ai_flags = AI_NUMERICHOST;
-				ret = pg_getaddrinfo_all(ch->hostaddr, portstr, &hint,
-										 &addrlist);
-				if (ret || !addrlist)
-				{
-					libpq_append_conn_error(conn, "could not parse network address \"%s\": %s",
-											ch->hostaddr, gai_strerror(ret));
-					goto keep_going;
-				}
-				break;
-
-			case CHT_UNIX_SOCKET:
-				hint.ai_family = AF_UNIX;
-				UNIXSOCK_PATH(portstr, thisport, ch->host);
-				if (strlen(portstr) >= UNIXSOCK_PATH_BUFLEN)
-				{
-					libpq_append_conn_error(conn, "Unix-domain socket path \"%s\" is too long (maximum %zu bytes)",
-											portstr,
-											(UNIXSOCK_PATH_BUFLEN - 1));
-					goto keep_going;
-				}
-
-				/*
-				 * NULL hostname tells pg_getaddrinfo_all to parse the service
-				 * name as a Unix-domain socket path.
-				 */
-				ret = pg_getaddrinfo_all(NULL, portstr, &hint,
-										 &addrlist);
-				if (ret || !addrlist)
-				{
-					libpq_append_conn_error(conn, "could not translate Unix-domain socket path \"%s\" to address: %s",
-											portstr, gai_strerror(ret));
-					goto keep_going;
-				}
-				break;
-		}
-
-		/*
-		 * Store a copy of the addrlist in private memory so we can perform
-		 * randomization for load balancing.
-		 */
-		ret = store_conn_addrinfo(conn, addrlist);
-		pg_freeaddrinfo_all(hint.ai_family, addrlist);
-		if (ret)
-			goto error_return;	/* message already logged */
+		ret = resolve_next_host(conn);
+		if (ret > 0)			/* non-fatal error; continue */
+			goto keep_going;
+		else if (ret < 0)		/* internal error, alloc failure, etc. */
+			goto error_return;
 
 		/*
 		 * If random load balancing is enabled we shuffle the addresses.
@@ -5208,6 +5096,140 @@ pqReleaseConnHosts(PGconn *conn)
 	}
 }
 
+/*
+ * Resolves the network addresses for the next pg_conn_host, advancing
+ * conn->whichhost as necessary, and stores them in the conn->addr array.
+ * Returns zero on success, positive for a host-specific failure (in which case
+ * we may attempt to resolve the next host, if any), and negative for a fatal
+ * error.
+ */
+static int
+resolve_next_host(PGconn *conn)
+{
+	pg_conn_host *ch;
+	struct addrinfo hint;
+	struct addrinfo *addrlist;
+	int			thisport;
+	int			ret;
+	char		portstr[MAXPGPATH];
+
+	if (conn->whichhost + 1 < conn->nconnhost)
+		conn->whichhost++;
+	else
+	{
+		/*
+		 * Oops, no more hosts.
+		 *
+		 * If we are trying to connect in "prefer-standby" mode, then drop the
+		 * standby requirement and start over. Don't do this for cancel
+		 * requests though, since we are certain the list of servers won't
+		 * change as the target_server_type option is not applicable to those
+		 * connections.
+		 *
+		 * Otherwise, an appropriate error message is already set up, so we
+		 * just need to set the right status.
+		 */
+		if (conn->target_server_type == SERVER_TYPE_PREFER_STANDBY &&
+			conn->nconnhost > 0 &&
+			!conn->cancelRequest)
+		{
+			conn->target_server_type = SERVER_TYPE_PREFER_STANDBY_PASS2;
+			conn->whichhost = 0;
+		}
+		else
+			return -1;
+	}
+
+	/* Drop any address info for previous host */
+	release_conn_addrinfo(conn);
+
+	ch = &conn->connhost[conn->whichhost];
+
+	/* Initialize hint structure */
+	MemSet(&hint, 0, sizeof(hint));
+	hint.ai_socktype = SOCK_STREAM;
+	hint.ai_family = AF_UNSPEC;
+
+	/* Figure out the port number we're going to use. */
+	if (ch->port == NULL || ch->port[0] == '\0')
+		thisport = DEF_PGPORT;
+	else
+	{
+		if (!pqParseIntParam(ch->port, &thisport, conn, "port"))
+			return -1;
+
+		if (thisport < 1 || thisport > 65535)
+		{
+			libpq_append_conn_error(conn, "invalid port number: \"%s\"", ch->port);
+			return 1;
+		}
+	}
+	snprintf(portstr, sizeof(portstr), "%d", thisport);
+
+	/* Use pg_getaddrinfo_all() to resolve the address */
+	switch (ch->type)
+	{
+		case CHT_HOST_NAME:
+			ret = pg_getaddrinfo_all(ch->host, portstr, &hint,
+									 &addrlist);
+			if (ret || !addrlist)
+			{
+				libpq_append_conn_error(conn, "could not translate host name \"%s\" to address: %s",
+										ch->host, gai_strerror(ret));
+				return 1;
+			}
+			break;
+
+		case CHT_HOST_ADDRESS:
+			hint.ai_flags = AI_NUMERICHOST;
+			ret = pg_getaddrinfo_all(ch->hostaddr, portstr, &hint,
+									 &addrlist);
+			if (ret || !addrlist)
+			{
+				libpq_append_conn_error(conn, "could not parse network address \"%s\": %s",
+										ch->hostaddr, gai_strerror(ret));
+				return 1;
+			}
+			break;
+
+		case CHT_UNIX_SOCKET:
+			hint.ai_family = AF_UNIX;
+			UNIXSOCK_PATH(portstr, thisport, ch->host);
+			if (strlen(portstr) >= UNIXSOCK_PATH_BUFLEN)
+			{
+				libpq_append_conn_error(conn, "Unix-domain socket path \"%s\" is too long (maximum %zu bytes)",
+										portstr,
+										(UNIXSOCK_PATH_BUFLEN - 1));
+				return 1;
+			}
+
+			/*
+			 * NULL hostname tells pg_getaddrinfo_all to parse the service
+			 * name as a Unix-domain socket path.
+			 */
+			ret = pg_getaddrinfo_all(NULL, portstr, &hint,
+									 &addrlist);
+			if (ret || !addrlist)
+			{
+				libpq_append_conn_error(conn, "could not translate Unix-domain socket path \"%s\" to address: %s",
+										portstr, gai_strerror(ret));
+				return 1;
+			}
+			break;
+	}
+
+	/*
+	 * Store a copy of the addrlist in private memory so we can perform
+	 * randomization for load balancing.
+	 */
+	ret = store_conn_addrinfo(conn, addrlist);
+	pg_freeaddrinfo_all(hint.ai_family, addrlist);
+	if (ret)
+		return -1;				/* message already logged */
+
+	return 0;
+}
+
 /*
  * store_conn_addrinfo
  *	 - copy addrinfo to PGconn object
-- 
2.43.0

