This is an automated email from the ASF dual-hosted git repository.
stoty pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/phoenix.git
The following commit(s) were added to refs/heads/master by this push:
new eb0ff97800 PHOENIX-7186 Support Square Brackets Notation for IPv6 in
JDBC URL (#2107)
eb0ff97800 is described below
commit eb0ff978006f332ec94eb2570227dbe19d2c8fc0
Author: Norbert Meszaros <[email protected]>
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 cdcfe9b0be..a2f3b9b30f 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;
@@ -119,6 +121,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);
@@ -143,7 +159,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()) {
@@ -521,10 +538,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 eb6e491fe9..1af2fd4180 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
@@ -569,6 +569,106 @@ public class PhoenixEmbeddedDriverTest {
assertFalse(ConnectionInfo.isSameName("user/[email protected]",
"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:"