This is an automated email from the ASF dual-hosted git repository. stoty pushed a commit to branch 5.2 in repository https://gitbox.apache.org/repos/asf/phoenix.git
The following commit(s) were added to refs/heads/5.2 by this push: new 5314a8a4a4 PHOENIX-7186 Support Square Brackets Notation for IPv6 in JDBC URL (#2107) 5314a8a4a4 is described below commit 5314a8a4a4180b46deec610d72374fc252d243a8 Author: Norbert Meszaros <meszinorbi2...@gmail.com> AuthorDate: Tue Apr 15 10:17:11 2025 +0200 PHOENIX-7186 Support Square Brackets Notation for IPv6 in JDBC URL (#2107) --- .../org/apache/phoenix/jdbc/ConnectionInfo.java | 22 ++++- .../phoenix/jdbc/PhoenixEmbeddedDriverTest.java | 100 +++++++++++++++++++++ 2 files changed, 120 insertions(+), 2 deletions(-) diff --git a/phoenix-core-client/src/main/java/org/apache/phoenix/jdbc/ConnectionInfo.java b/phoenix-core-client/src/main/java/org/apache/phoenix/jdbc/ConnectionInfo.java index faaa9841ec..4c0c4fefb6 100644 --- a/phoenix-core-client/src/main/java/org/apache/phoenix/jdbc/ConnectionInfo.java +++ b/phoenix-core-client/src/main/java/org/apache/phoenix/jdbc/ConnectionInfo.java @@ -25,6 +25,8 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Properties; import java.util.StringTokenizer; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hbase.security.User; @@ -116,6 +118,20 @@ public abstract class ConnectionInfo { return escaped.replaceAll("\\\\:", "="); } + protected static String escapeIPv6Literals(String unescaped) { + String regex = "(\\[.*?\\])"; + Pattern pattern = Pattern.compile(regex); + Matcher matcher = pattern.matcher(unescaped); + StringBuffer result = new StringBuffer(); + while (matcher.find()) { + String matchedText = matcher.group(1); + String modifiedText = matchedText.replace(":", "~"); + matcher.appendReplacement(result, modifiedText); + } + matcher.appendTail(result); + return result.toString(); + } + public static ConnectionInfo createNoLogin(String url, ReadOnlyProps props, Properties info) throws SQLException { return create(url, getCachedConfiguration(), props, info, true); @@ -140,7 +156,8 @@ public abstract class ConnectionInfo { ReadOnlyProps props, Properties info, boolean doNotLogin) throws SQLException { // registry-independent URL preprocessing url = url == null ? "" : url; - url = unescape(url); + boolean isIPv6 = url.contains("[") && url.contains("]"); + url = isIPv6 ? unescape(escapeIPv6Literals(url)) : unescape(url); // Assume missing prefix if (url.isEmpty()) { @@ -509,10 +526,11 @@ public abstract class ConnectionInfo { String[] normalizedParts = new String[quorumParts.length]; for (int i = 0; i < quorumParts.length; i++) { String[] hostAndPort = quorumParts[i].trim().split(":"); + hostAndPort[0] = hostAndPort[0].replace('~', ':'); if (hostAndPort.length == 1) { normalizedParts[i] = hostAndPort[0].trim().toLowerCase() + ":" + defaultPort; } else if (hostAndPort.length == 2) { - normalizedParts[i] = quorumParts[i].trim().toLowerCase(); + normalizedParts[i] = String.join(":", hostAndPort).trim().toLowerCase(); } else { throw getMalFormedUrlException(url); } diff --git a/phoenix-core/src/test/java/org/apache/phoenix/jdbc/PhoenixEmbeddedDriverTest.java b/phoenix-core/src/test/java/org/apache/phoenix/jdbc/PhoenixEmbeddedDriverTest.java index cf41c1a45f..27b40754b0 100644 --- a/phoenix-core/src/test/java/org/apache/phoenix/jdbc/PhoenixEmbeddedDriverTest.java +++ b/phoenix-core/src/test/java/org/apache/phoenix/jdbc/PhoenixEmbeddedDriverTest.java @@ -566,6 +566,106 @@ public class PhoenixEmbeddedDriverTest { assertFalse(ConnectionInfo.isSameName("user/foo...@apache.net", "user/_HOST", "foobar", "APACHE.ORG")); } + @Test + public void testMasterIPv6() throws SQLException { + assumeTrue(VersionInfo.compareVersion(VersionInfo.getVersion(), "2.3.0") >= 0); + try { + Configuration config = + HBaseFactoryProvider.getConfigurationFactory().getConfiguration(); + config.set("hbase.client.registry.impl", + "org.apache.hadoop.hbase.client.MasterRegistry"); + ConnectionInfo.create("jdbc:phoenix+master", config, null, null); + fail("Should have thrown exception"); + } catch (SQLException e) { + } + + Configuration config = HBaseFactoryProvider.getConfigurationFactory().getConfiguration(); + config.set("hbase.client.registry.impl", "org.apache.hadoop.hbase.client.MasterRegistry"); + config.set("hbase.master.port", "17000"); + MasterConnectionInfo info = + (MasterConnectionInfo) ConnectionInfo.create( + "jdbc:phoenix+master:[::1],[\\:\\:2]", config, null, null); + assertEquals(info.getBoostrapServers(), "[::1]:17000,[::2]:17000"); + + config = HBaseFactoryProvider.getConfigurationFactory().getConfiguration(); + config.set("hbase.client.registry.impl", "org.apache.hadoop.hbase.client.MasterRegistry"); + info = + (MasterConnectionInfo) ConnectionInfo.create( + "jdbc:phoenix+master:[::1]\\:123,[\\:\\:2]\\:234", config, null, null); + assertEquals(info.getBoostrapServers(), "[::1]:123,[::2]:234"); + } + + @Test + public void testRPCIPv6() throws SQLException{ + Configuration config = HBaseFactoryProvider.getConfigurationFactory().getConfiguration(); + config.set("hbase.client.registry.impl", + "org.apache.hadoop.hbase.client.RpcConnectionRegistry"); + RPCConnectionInfo info = + (RPCConnectionInfo) ConnectionInfo.create( + "jdbc:phoenix+rpc:[::1]\\:123,[\\:\\:2]\\::234", config, + null, null); + assertEquals("[::1]:123,[::2]:234", info.getBoostrapServers()); + } + + @Test + public void testZkIPv6() throws Exception { + ConnectionInfo connectionInfo = ConnectionInfo.create("jdbc:phoenix+zk:" + + "[::1],127.23.45.678\\:7634,v3\\:1,host123.48576\\:723:/hbase;" + + "test=true", null, null); + ReadOnlyProps props = connectionInfo.asProps(); + assertEquals("127.23.45.678:7634,[::1]:2181,host123.48576:723,v3:1", + props.get(HConstants.ZOOKEEPER_QUORUM)); + + connectionInfo = ConnectionInfo.create("jdbc:phoenix+zk:" + + "[::1]\\:2181,127.23.45.678\\:7634,v3\\:1,host123.48576\\:723:/hbase;" + + "test=true", null, null); + props = connectionInfo.asProps(); + assertEquals("127.23.45.678:7634,[::1]:2181,host123.48576:723,v3:1", + + props.get(HConstants.ZOOKEEPER_QUORUM)); + connectionInfo = ConnectionInfo.create("jdbc:phoenix+zk:" + + "[fe:80::],127.23.45.678\\:7634,v3\\:1,host123.48576\\:723:/hbase;" + + "test=true", null, null); + props = connectionInfo.asProps(); + assertEquals("127.23.45.678:7634,[fe:80::]:2181,host123.48576:723,v3:1", + props.get(HConstants.ZOOKEEPER_QUORUM)); + + connectionInfo = ConnectionInfo.create("jdbc:phoenix+zk:" + + "[fe:80::]\\:2181,127.23.45.678\\:7634,v3\\:1,host123.48576\\:723:/hbase;" + + "test=true", null, null); + props = connectionInfo.asProps(); + assertEquals("127.23.45.678:7634,[fe:80::]:2181,host123.48576:723,v3:1", + props.get(HConstants.ZOOKEEPER_QUORUM)); + + connectionInfo = ConnectionInfo.create("jdbc:phoenix+zk:" + + "[fe\\:80\\:\\:],127.23.45.678\\:7634,v3\\:1,host123.48576\\:723:/hbase;" + + "test=true", null, null); + props = connectionInfo.asProps(); + assertEquals("127.23.45.678:7634,[fe:80::]:2181,host123.48576:723,v3:1", + props.get(HConstants.ZOOKEEPER_QUORUM)); + + connectionInfo = ConnectionInfo.create("jdbc:phoenix+zk:" + + "[fe\\:80\\:\\:]\\:2181,127.23.45.678\\:7634,v3\\:1,host123.48576\\:723:/hbase;" + + "test=true", null, null); + props = connectionInfo.asProps(); + assertEquals("127.23.45.678:7634,[fe:80::]:2181,host123.48576:723,v3:1", + props.get(HConstants.ZOOKEEPER_QUORUM)); + + connectionInfo = ConnectionInfo.create("jdbc:phoenix+zk:" + + "[fe:80::2]\\:2181,[2001:db8:3c4d:15::1a2f:1a2b]\\:7634,v3\\:1,host123.48576\\:723:/hbase;" + + "test=true", null, null); + props = connectionInfo.asProps(); + assertEquals("[2001:db8:3c4d:15::1a2f:1a2b]:7634,[fe:80::2]:2181,host123.48576:723,v3:1", + props.get(HConstants.ZOOKEEPER_QUORUM)); + + connectionInfo = ConnectionInfo.create("jdbc:phoenix+zk:" + + "[fe:80::2]\\:2181,[2001\\:db8\\:3c4d\\:15\\:\\:1a2f\\:1a2b]\\:7634,v3\\:1,host123.48576\\:723:/hbase;" + + "test=true", null, null); + props = connectionInfo.asProps(); + assertEquals("[2001:db8:3c4d:15::1a2f:1a2b]:7634,[fe:80::2]:2181,host123.48576:723,v3:1", + props.get(HConstants.ZOOKEEPER_QUORUM)); + } + @Test public void testZkQuorumConfigs() throws Exception { ConnectionInfo connectionInfo = ConnectionInfo.create("jdbc:phoenix+zk:"